Skip to content

Instantly share code, notes, and snippets.

@Zerg00s
Created October 30, 2021 13:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zerg00s/d0adaeea4c7b5119528e991aa2b2417f to your computer and use it in GitHub Desktop.
Save Zerg00s/d0adaeea4c7b5119528e991aa2b2417f to your computer and use it in GitHub Desktop.
Deploy SharePoint User Profiles Using PowerShell and Internet Explorer
# ===========================================================#
# Internet explorer Macros
# For creating a bunch of user properties in bulk
# using Internet Explorer COM
# ----
# User_Profile_Properties.csv defines properties to be
# created.
# ===========================================================#
#------------------------------------------------------------#
# Used for preparing DEV/QA/UAT envrionments
# Warning: Not for use in Production
#------------------------------------------------------------#
# TARGET TENANT
$username = 'Admin@Contoso.onmicrosoft.com'
$password = 'Secret password'
$siteUrl = 'https://Contoso.sharepoint.com'
# ===========================================================#
# PROPERTIES CHEAT SHEET
# ===========================================================#
#------------- User Profile Privacy Types -------------------#
# "Only Me",
# "Everyone"
#------------- User Profile Policy Types -------------------#
# "Required",
# "Optional",
# "Disabled"
#------------- User Profile Property Types -------------------#
# "big integer",
# "binary",
# "boolean",
# "date",
# "datenoyear",
# "date time",
# "Email",
# "float",
# "HTML",
# "integer",
# "Person",
# "string (Multi Value)",
# "string (Single Value)",
# "timezone",
# "unique identifier",
# "URL"
# ===========================================================#
$ErrorActionPreference = 'Stop'
$ie = New-Object -com InternetExplorer.Application
$ie.visible=$True
$ie.navigate("$siteUrl/_layouts/15/TenantProfileAdmin/MgrProperty.aspx?ProfileType=User&ApplicationID=00000000%2D0000%2D0000%2D0000%2D000000000000")
while($ie.Busy) { Start-Sleep -Milliseconds 100 }
Start-Sleep -Milliseconds 300
try{
$link=$ie.Document.getElementsByTagName('A') | Where-Object {$_.textContent -eq "Click here to sign in with a different account to this site."}
if($link -ne $null){
$link.click()
}
} catch{}
while($ie.Busy) { Start-Sleep -Milliseconds 100 }
$PickAccountDiv = $null
$UserProfilesAnchor = $null
while($PickAccountDiv -eq $null){
$PickAccountDiv = $ie.Document.IHTMLDocument3_getElementsByTagName('DIV') | Where-Object {$_.textContent -eq "Pick an account"}
$UserProfilesAnchor = $ie.Document.IHTMLDocument3_getElementsByTagName('A') | Where-Object {$_.textContent -ne $null -and $_.textContent.Contains("User Profiles")}
if($UserProfilesAnchor -ne $null){
break;
}
}
if($UserProfilesAnchor -eq $null){
$otherAccountDiv = $ie.Document.IHTMLDocument3_getElementById("otherTile")
if($otherAccountDiv -eq $null){
return;
}
$otherAccountDiv.click()
while($ie.ReadyState -ne 4) { start-sleep -m 100 }
Start-Sleep -Milliseconds 300
$loginInput = $ie.Document.IHTMLDocument3_getElementsByName("loginfmt")
if($loginInput -eq $null){
return;
}
$loginInput[0].value = $username
# knockout.js model does not update in the browser when PowerShell used to click the "Next" button.
# We need to click "Next" button with the mouse
# $nextButton = $ie.Document.IHTMLDocument3_getElementsByTagName('INPUT') | Where-Object {$_.value -eq "Next"}
# if($nextButton -eq $null){
# return;
# }
# $nextButton.click()
$EnterPasswordDiv = $null
while($EnterPasswordDiv -eq $null) {
start-sleep -m 100
$EnterPasswordDiv = $ie.Document.IHTMLDocument3_getElementsByTagName('DIV') | Where-Object {$_.textContent -eq "Enter password"}
}
Start-Sleep -Milliseconds 300
$passwordInput = $ie.Document.IHTMLDocument3_getElementsByName("passwd")
if($passwordInput -eq $null){
return;
}
$passwordInput[0].value = $password
}
$StaySignedIn = $null
$UserProfilesAnchor = $null
while($StaySignedIn -eq $null -and $UserProfilesAnchor -eq $null) {
$UserProfilesAnchor = $ie.Document.IHTMLDocument3_getElementsByTagName('A') | Where-Object {$_.textContent -ne $null -and $_.textContent.Contains("User Profiles")}
$StaySignedIn = $ie.Document.IHTMLDocument3_getElementsByTagName('DIV') | Where-Object {$_.textContent -eq "Stay signed in?"}
start-sleep -m 100
if($StaySignedIn.textContent -eq $null){
$StaySignedIn = $null
}
start-sleep -m 100
if($StaySignedIn -ne $null){
$YesInput = $ie.Document.IHTMLDocument3_getElementsByTagName('INPUT') | Where-Object {$_.value -eq "Yes"}
$YesInput.click()
}
}
while($ie.Busy -Or $ie.ReadyState -ne 4) { Start-Sleep -Milliseconds 100 }
Write-Host "User Profiles Page Loaded" -ForegroundColor Green
function VerifyPropertyExists() {
param($propertyName)
Write-host "Searching for property $propertyName" -ForegroundColor White
$ListOfPropertiesHtml = $null
while($ListOfPropertiesHtml -eq $null){
$ListOfPropertiesHtml = $ie.Document.IHTMLDocument3_getElementById("ViewPropertiesSRCHRES").innerHTML
Start-Sleep -Seconds 1
}
$PropertyExists = $ListOfPropertiesHtml.Contains($propertyName)
if($PropertyExists){
Write-host "Property $propertyName already exists. Skipping..." -ForegroundColor Cyan
}else{
Write-host "Property $propertyName does not exist. Creating..." -ForegroundColor Yellow
}
return $PropertyExists
}
function EnsureProperty() {
param($Property)
if($false -eq (VerifyPropertyExists -propertyName $Property.Name) ){
$newPropertyButton = $ie.Document.IHTMLDocument3_getElementsByTagName("A") | Where-Object {$_.textContent -eq "New Property"}
$newPropertyButton.click()
while($ie.Busy -Or $ie.ReadyState -ne 4) { Start-Sleep -Milliseconds 100 }
$propNameInptut = $null
while($propNameInptut -eq $null){
$propNameInptut = $ie.Document.IHTMLDocument3_getElementById('ctl00$ctl00$PlaceHolderContentArea$PlaceHolderMain$txtboxName')
}
$propNameInptut.value = $Property.Name
Write-Host "New Property Page Loaded $($Property.Name)" -ForegroundColor Cyan
$propDisplayNameInptut = $ie.Document.IHTMLDocument3_getElementById('ctl00$ctl00$PlaceHolderContentArea$PlaceHolderMain$txtboxDisplayName')
$propDisplayNameInptut.value = $Property.DisplayName
$ready = $false
while($ready -eq $false){
$TypeSelect = $ie.Document.IHTMLDocument3_getElementById('ctl00_ctl00_PlaceHolderContentArea_PlaceHolderMain_drpType')
try{
if($TypeSelect.options.length -ne 0){
# Select options contain special characters. Must use this approach:
$matchedArrayItem = ($TypeSelect.options | % {$_.InnerText}) | Where-Object {$_.contains($Property.Type)}
$indexOfOption = ($TypeSelect.options | % {$_.InnerText}).indexOf($matchedArrayItem)
$TypeSelect.focus()
$TypeSelect.selectedIndex = $indexOfOption
$ready = $true
}else{
$ready = $false
Write-Host "Type Drop-down is not yet ready. Waiting..." -ForegroundColor Yellow
Start-Sleep -Seconds 2
}
}
catch{
$ready = $false
Write-Host "Type Drop-down is not yet ready. Waiting..." -ForegroundColor Yellow
Start-Sleep -Seconds 2
}
}
# Fire postback and pass the Type selector ID:
$TypeSelect.focus()
$ie.document.Script.execScript("__doPostBack('ctl00`$ctl00`$PlaceHolderContentArea`$PlaceHolderMain`$drpType','')")
while($ie.Busy -Or $ie.ReadyState -ne 4) { Start-Sleep -Seconds 2 }
# Set Property Length
if($Property.Length){
$propLengthInptut = $ie.Document.IHTMLDocument3_getElementById('ctl00$ctl00$PlaceHolderContentArea$PlaceHolderMain$txtboxLength')
$propLengthInptut.value = $Property.Length
}
if ($Property.TermSet){
$TermSelected = $false
while($TermSelected -eq $false){
$TermSetSelect = $ie.Document.IHTMLDocument3_getElementById('ctl00_ctl00_PlaceHolderContentArea_PlaceHolderMain_drpTaxonomyTermSetPicker')
if($TermSetSelect -eq $null -or $TermSetSelect.options -eq $null){
$TermSelected = $false
Write-Host "Terms Drop-down is not yet ready. Waiting..." -ForegroundColor Yellow
Start-Sleep -Seconds 2
}else{
$indexOfOption = ([string[]]($TermSetSelect.options | % {$_.innerHTML})).IndexOf($Property.TermSet)
$TermSetSelect.selectedIndex = $indexOfOption
$TermSelected = $true
}
}
}
$PolicySelect = $ie.Document.IHTMLDocument3_getElementById('ctl00_ctl00_PlaceHolderContentArea_PlaceHolderMain_drpPolicy')
$indexOfOption = ([string[]]($PolicySelect.options | % {$_.innerHTML})).IndexOf($Property.PolicyType)
$PolicySelect.selectedIndex = $indexOfOption
$PrivacySelect = $ie.Document.IHTMLDocument3_getElementById('ctl00_ctl00_PlaceHolderContentArea_PlaceHolderMain_drpDefaultPrivacy')
$indexOfOption = ([string[]]($PrivacySelect.options | % {$_.innerHTML})).IndexOf($Property.Privacy)
$PrivacySelect.selectedIndex = $indexOfOption
$AllowUserEditCheckbox = $ie.Document.IHTMLDocument3_getElementById('ctl00_ctl00_PlaceHolderContentArea_PlaceHolderMain_chkboxUserEditable')
$AllowUserEditCheckbox.checked = [boolean]$Property.AllowUserEdit
$ShowInProfiletCheckbox = $ie.Document.IHTMLDocument3_getElementById('ctl00_ctl00_PlaceHolderContentArea_PlaceHolderMain_chkboxIsVisibleOnViewer')
$ShowInProfiletCheckbox.checked = [boolean]$Property.ShowInProfile
$ShowOnEditPageCheckbox = $ie.Document.IHTMLDocument3_getElementById('ctl00_ctl00_PlaceHolderContentArea_PlaceHolderMain_chkboxIsVisibleOnEditor')
$ShowOnEditPageCheckbox.checked = [boolean]$Property.ShowOnEditPage
if($Property.MaximumValues){
$MaximumValuesSelect = $ie.Document.IHTMLDocument3_getElementById('ctl00_ctl00_PlaceHolderContentArea_PlaceHolderMain_drpMaxShown')
$indexOfOption = ([string[]]($MaximumValuesSelect.options | % {$_.innerHTML})).IndexOf($Property.MaximumValues)
$MaximumValuesSelect.selectedIndex = $indexOfOption
}
Start-Sleep -Milliseconds 2000
$OkButton = $ie.Document.IHTMLDocument3_getElementById('ctl00_ctl00_PlaceHolderContentArea_PlaceHolderMain_btnOK')
$OkButton.click()
Write-Host "OK button clicked!" -ForegroundColor Magenta
while($ie.Busy -Or $ie.ReadyState -ne 4) { Start-Sleep -Milliseconds 100 }
Write-host Property $Property.Name created -foregroundColor Green
}
}
$properties = Import-Csv .\User_Profile_Properties.csv
foreach($Property in $properties){
# Skip vorUPPairing deployment:
if($Property.Name -notmatch "vorUPPairing"){
EnsureProperty -Property $Property
}
}
Write-Host "Done. All properties are created" -ForegroundColor Green
#-----------------------------------------------------------------------------------
# CONNECTING TO OFFICE 365 & AZURE AD
#-----------------------------------------------------------------------------------
# Install-Module -Name MSOnline
$Account = "Admin@Contoso.onmicrosoft.com"
$Password = 'SecretPassword'
$securePass = ConvertTo-SecureString -String $Password -AsPlainText -Force
$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $Account, $securePass
Connect-MsolService -Credential $credentials
#-----------------------------------------------------------------------------------
# GET DEVELOPER'S E3 LICENSE ID:
#-----------------------------------------------------------------------------------
$License = Get-MsolAccountSku | Where-Object {$_.AccountSkuId -match "DEVELOPERPACK"}
$LicenseSKUid = $License.AccountSkuId
#-----------------------------------------------------------------------------------
# CREATE TEST USERS WITH LICENSES
#-----------------------------------------------------------------------------------
$domain = (Get-MsolDomain).Name
Add-Type -AssemblyName System.Web
$TestUsers = @(
@{FirstName = "Cierra"; LastName = "Hooper"; MiddleName="Cash"; PreferredName="Cierra Hooper"; JobTitle = "Administrative Assistant"},
@{FirstName = "Alden"; LastName = "Mason"; MiddleName=""; PreferredName="Alden Mason"; JobTitle = "Clerk"},
@{FirstName = "Kierra"; LastName = "Frost"; MiddleName=""; PreferredName="Kierra Frost"; JobTitle = "Copy Center Professional"},
@{FirstName = "Pierre"; LastName = "Horn"; MiddleName="Drew"; PreferredName="Pierre Horn"; JobTitle = "Document Coder"},
@{FirstName = "Thomas"; LastName = "Obrien"; MiddleName=""; PreferredName="Thomas Obrien"; JobTitle = "File Clerk"},
@{FirstName = "Miranda"; LastName = "Bautista"; MiddleName="Ellice"; PreferredName="Miranda Bautista"; obTitle = "Legal Aide/Assistant"},
@{FirstName = "Bradyn"; LastName = "Contreras"; MiddleName=""; PreferredName="Bradyn Contreras"; JobTitle = "Legal Secretary"},
@{FirstName = "Alvaro"; LastName = "Scott"; MiddleName="Aryn"; PreferredName="Alvaro Scott"; JobTitle = "Mailroom Personnel"},
@{FirstName = "Lilliana"; LastName = "Zuniga"; MiddleName=""; PreferredName="Lilliana Lilliana"; JobTitle = "Legal Records Manager"},
@{FirstName = "Manuel"; LastName = "Yoder"; MiddleName="Garrison"; PreferredName="Manuel Manuel"; JobTitle = "Legal Services Director"}
)
try {
# Accounts might already exist
$accounts = @()
foreach ($testUser in $TestUsers){
Write-host $testUser
Write-host "$($testUser.FirstName).$($testUser.LastName)@$domain"
$StrongPassword = [System.Web.Security.Membership]::GeneratePassword(12, 3)
$generatedAccount = New-MsolUser -LicenseAssignment $LicenseSKUid -Password $StrongPassword -UserPrincipalName "$($testUser.FirstName).$($testUser.LastName)@$domain" -DisplayName "$($testUser.FirstName) $($testUser.LastName)" -FirstName $testUser.FirstName -LastName $testUser.LastName -UsageLocation "CA" -City Toronto -Title $testUser.JobTitle -Office Adelaide -PhoneNumber 4168392211 -MobilePhone 4164442211 -Country Canada -Department PMO -PostalCode "M4M2k8" -StreetAddress "210 King st." -State ON -Fax 4162221144 -PasswordNeverExpires $true -ForceChangePassword $false -StrongPasswordRequired $false
$accounts += $generatedAccount
}
#-----------------------------------------------------------------------------------
# STOP HERE AND RECORD USERS' PASSWORDS
#-----------------------------------------------------------------------------------
$accounts
}
catch{
Write-host $_
}
#-----------------------------------------------------------------------------------
# Connect to SharePoint Online via PnP
#-----------------------------------------------------------------------------------
$TenantUrl = "https://$($domain -replace ".onmicrosoft.com")-admin.sharepoint.com"
$RootSiteUrl = "https://$($domain -replace ".onmicrosoft.com").sharepoint.com"
Connect-PnPOnline -Url $TenantUrl -Credentials $credentials
$tenantAdminConnection = Connect-PnPOnline -Url $TenantUrl -Credentials $credentials -ReturnConnection
#-----------------------------------------------------------------------------------
# Populate custom user profile properties
#-----------------------------------------------------------------------------------
foreach ($testUser in $TestUsers){
$userPrincipal = "$($testUser.FirstName).$($testUser.LastName)@$domain"
write-host "Populating $userPrincipal with test values" -foregroundColor Yellow
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPMiddleName -Value $testUser.MiddleName
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName PreferredName -Value $testUser.PreferredName
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPEmployeeId -Value "123453"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPRoomId -Value "654321"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPTimekeeperID -Value 12345
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPClassification -Value "Staff"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPLinkedIn -Value "https://www.linkedin.com/in/molodtsovd/"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPBarCourtAdmissions -Values "California", "Colorado"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPNotary -Value $true
# THIS IS ALREADY SET:
# Set-PnPUserProfileProperty -account $userPrincipal -PropertyName SPS-JobTitle -Value "Tester"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName SPS-HireDate -Value "2018-06-07"
# THIS IS ALREADY SET:
# Set-PnPUserProfileProperty -account $userPrincipal -PropertyName Office -Value "01-1-001"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName SPS-Location -Value "Tokyo"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPCities -Value "Houston"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPDepartment -Value "Corporate"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPDepartmentHierarchy -Value "Administration"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPPractice -Values "Litigation"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPLanguages -Values "Spanish"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPLawSchool -Values "Brooklyn Law School"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPUndergraduateInstitutions -Values "York College"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPUndergraduateDegrees -Values "B.A."
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPGraduateInstitutions -Values "Yeshiva University"
Set-PnPUserProfileProperty -account $userPrincipal -PropertyName vorUPGraduateDegrees -Values "LL.M."
for($i=1;$i -le 20;$i++){
# Ignore the Vorys Classic properties
# Set-PnPUserProfileProperty -account $userPrincipal -PropertyName "vorUPPairing$($i.ToString("00"))" -Value "i:0#.f|membership|$userPrincipal"
}
}
Disconnect-PnPOnline
Name DisplayName Type Length TermSet PolicyType Privacy AllowUserEdit ShowInProfile MaximumValues ShowOnEditPage
vorUPEmployeeId EmployeeID string (Single Value) 10 Required Everyone
vorUPRoomId RoomID string (Single Value) 10 Optional Everyone
vorUPPractice Practices string (Multi Value) 25 Practice Optional Everyone TRUE 3 TRUE
vorUPDepartment Departments (deprecated) string (Multi Value) 25 Department Optional Everyone TRUE 3 TRUE
vorUPDepartmentHierarchy Departments string (Multi Value) 25 Department Hierarchy Optional Everyone TRUE 3 TRUE
vorUPLinkedIn LinkedIn URL Optional Everyone TRUE TRUE TRUE
vorUPMiddleName Middle Name string (Single Value) 25 Optional Everyone TRUE TRUE
vorUPExtension Extension string (Single Value) 25 Optional Everyone TRUE TRUE
vorUPClassification Classification ‪string (Multi Value)‬ 25 Classification Optional Everyone TRUE 3 TRUE
vorUPBarCourtAdmissions Bar & Court Admissions ‪string (Multi Value)‬ 100 Bar & Court Admissions Optional Everyone TRUE 3 TRUE
vorUPLanguages Languages ‪string (Multi Value)‬ 25 Languages Optional Everyone TRUE TRUE 3 TRUE
vorUPUndergraduateDegrees Undergraduate Degrees ‪string (Multi Value)‬ 100 Undergraduate Degrees Optional Everyone TRUE 3 TRUE
vorUPUndergraduateInstitutions Undergraduate Institutions ‪string (Multi Value)‬ 100 Academic Institutions Optional Everyone TRUE 3 TRUE
vorUPGraduateDegrees Graduate Degrees ‪string (Multi Value)‬ 100 Graduate Degrees Optional Everyone TRUE 3 TRUE
vorUPGraduateInstitutions Graduate Institutions ‪string (Multi Value)‬ 100 Academic Institutions Optional Everyone TRUE 3 TRUE
vorUPLawSchool Law School ‪string (Multi Value)‬ 25 Law Schools Optional Everyone TRUE 3 TRUE
vorUPTimeKeeperID Timekeeper ID string (Single Value) 25 Optional Everyone TRUE TRUE
vorUPNotary Notary boolean Optional Everyone TRUE TRUE TRUE
vorUPCities Cities ‪string (Multi Value)‬ 25 Office Optional Everyone TRUE 3 TRUE
vorUPPairing01 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing02 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing03 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing04 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing05 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing06 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing07 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing08 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing09 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing10 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing11 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing12 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing13 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing 14 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing15 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing16 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing17 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing18 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing19 Pairing Person Optional Everyone TRUE TRUE
vorUPPairing20 Pairing Person Optional Everyone TRUE TRUE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment