Exporting Assets from Freshservice using Powershell

I recently got a request to write an automation to check the warranty status of all our computers. Simply request, huh?

I decided the best way was to write a script that would export all the assets from our Freshservice instance and get the serial number of them. The next stage is to write a script that actually checks the warranty status with HP, but currently their warranty API is offline so that will come later. Right now we’re only exporting the assets and getting the serials from Freshservice.

You will need to find your API key in your profile for Fresh, and your instance URL which you already should now. The next thing to check for is if your ID for serial number is the same, in our case it’s “serial_number_26000476017” but I guess that may vary.

#################################################
# Export all assets and get serial to check with HP warranty
#################################################

# API Key
$FDApiKey="XXXXXX"

#################################################
# Prep
$pair = "$($FDApiKey):$($FDApiKey)"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
$basicAuthValue = "Basic $base64"
$FDHeaders = @{ Authorization = $basicAuthValue }
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::TLS12
# The Doing part
$FDAssetData = ""
$Output = @()
$i = 1
do
	{
		$FDAssetDatatmp = ""
		$FDBaseEndpointSummary = "https://<your fresh URL>/cmdb/items.json?page=$i"
		# You may want to write this out to check how many pages it loads
		# write-host $FDBaseEndpointSummary
		$FDAssetData = Invoke-WebRequest -uri $FDBaseEndpointSummary -Headers $FDHeaders -Method GET -ContentType application/json | ConvertFrom-Json
		$Output = $Output + $FDAssetData
		$i++
	} while($FDAssetData)
Write-host -foregroundcolor Cyan "$i pages imported..."

#Now let's go through every row and only filter out the HP computers and get the serial number.
foreach($Outputrow in $Output)
{
    $name = ""
    $serial = ""
    $name = $Outputrow.name
    # This levelfield for serial number may not be the same for everyone, please check this up!
	$serial = $Outputrow.levelfield_values.serial_number_26000476017
    # Using the example of HP here
	if($Outputrow.product_name -like "HP*")
        {
            write-host -foregroundcolor Green "Here you can check warranty for $name with serial $serial..."
        }
    else
        {
            write-host -foregroundcolor Yellow "Asset $name is not HP computer."
        }
}

Credit goes to Mark Wilkinson for writing the basics of this in a forum post here.

Check If Connected to SPOService in Script

SCENARIO
When executing SharePoint Online scripts you need to be connected to your “admin” site or the script will just fail if you’re not.

PROBLEM
When writing a script you can’t assume that you’re already connected to your SPO tenant and unlike the “msolservice” connect call you need to specify your “admin” URL which can be quite long. But sometimes you’re already connected in the Powershell session.

SOLUTION
Writing this little thing in the start of your script will check if you’re connected to the admin site and if not will call the connect-sposervice command with the URL already set.

# First we reset the sitecheck to avoid having an old result
$sitecheck=""
# This is the address of your SPO admin site
$adminurl = "https://[your tenant name]-admin.sharepoint.com"
# Now we try to get the SPOSITE info for the admin site
Try { $sitecheck = get-sposite $adminurl }
# If we get this server exception for any reason, the service isn't available and we need to take action, in this case
# write it to the console and then connect to the SPO service.
Catch [Microsoft.SharePoint.Client.ServerException]
{
Write-Host -foreground Yellow "You are not connected!"
connect-sposervice $adminurl
}

Issues changing ImmutableID with error FederatedUser.SourceAnchor

Recently ran into an issue where a user in the on-prem AD had been deleted unintentionally and in the next sync his user went along with his mailbox.
Googling around I found a helpful article how to best go about restoring this. It’s basically about creating a new on-prem users and setting the new GUID on the recovered AzureAD user so AzureAD Connect can tie them together.
However, when trying to set the new “ImmutableID” with “set-msoluser” I got this error:
Set-MsolUser : You must provide a required property: Parameter name: FederatedUser.SourceAnchor

Took alot of Googling to realise what was wrong! The issue here is that you can’t set a new ImmutableID on a user in a Federated domain! So the trick here was to change the user to an “onmicrosoft” user, change the ImmutableID and then changing it back to the federated domain!

# Checking the original ImmutableID
get-msoluser -UserPrincipalName [email protected] | select *immutableid*
# Changing it to a "onmicrosoft" UPN
set-MsolUserPrincipalName -UserPrincipalName [email protected] -NewUserPrincipalName [email protected]
# Setting a new Immutable ID from on-prem AD
set-MsolUser –UserPrincipalName [email protected] -ImmutableId "Z/-XGv2W4kWPM1mR/ddSdn!)"
# Check that the change was applied
get-msoluser -UserPrincipalName [email protected] | select *immutableid*
# Changing it back to the original UPN
set-MsolUserPrincipalName -UserPrincipalName [email protected] -NewUserPrincipalName [email protected]
# Checking that the UPN is now correct and the correct ImmutableID is applied
get-msoluser -UserPrincipalName [email protected] | select *immutableid*

Hope that saves someone some headache.

SharePoint 2016 Installation Errors

SCENARIO
You’re trying to install SharePoint 2016 on a Windows 2016 server and thinks just aren’t going well.

PROBLEM
To be honest I don’t know how else to explain the problem in any other way than Microsoft’s Windows Server 2016 team was in a feud over lunchboxes with the SharePoint 2016 devs because there is no other way to describe the complete incompatibility between the two!

SOLUTION
I’d say “Google it!” but that’s probably what got you here in the first place!
The first problem is the prerequisite installer that can’t configure Windows IIS role or download things. Fret not for there is plenty of help to find. When first running the prereq you’ll probably get this error: “Web Server (IIS) Role: configuration error”. To configure the IIS use this Powershell :

Add-WindowsFeature Web-Server,windows-identity-foundation,`NET-Framework-45-ASPNET,Web-Mgmt-Console,Web-Mgmt-Compat,Web-Metabase,Web-Lgcy-Mgmt-Console,Web-Lgcy-Scripting,Web-Mgmt-Tools,Web-WMI,Web-Common-HTTP,NET-HTTP-Activation,NET-Non-HTTP-Activ,NET-WCF-HTTP-Activation45 -Source 'Q:\sources\sxs'

Make sure to edit the source file to the Windows Server 2016 ISO!

The next place you should look at is this blog by the Microsoft Field Engineer Nik. Although be careful about some of his links as those are outdated and replaced with new versions, although downloading the version he’s linking will still work. He even provides a script that will run the Powershell to configure everything. Why this isn’t on the SharePoint 2016 ISO is beyond me!
But even when downloading all of that and installing it properly I was still faced with this error when trying to setup the farm: “New-SPConfigurationDatabase : One or more types failed to load. Please refer to the upgrade log for more details.“. Going through the install log I found this: “SharePoint Foundation Upgrade SPSiteWssSequence ajywy ERROR Exception: Could not load file or assembly ‘Microsoft.Data.OData, Version=5.6.0.0, Culture=neutral, PublicKeyToken=31bc3856cd365e35’ or one of its dependencies. The system cannot find the file specified.

It seems that the WCF prerequisite file when installed using the Powershell method of manually downloading and installing it! Fortunately the quick fix is to find the file “WcfDataServices.exe” in your profile directory (i.e NOT the one you downloaded!), running it and choosing “Repair”. Only then did SharePoint 2016 install properly!

Check E-mail Addresses From File

SCENARIO
You’re handed a list of e-mail address for mass mailing from HR and they need to verify that all e-mail addresses are valid and won’t bounce “like last time”.

PROBLEM
There are a few problems with this. One is the fact that not all e-mail addresses are the primary e-mail address and won’t show up in a normal search.

SOLUTION
I put this little script together that will first connect to your MS Online tenant, then read all MSOL users into an array, import the CSV file containing the employees, go through each row and check that the e-mail address from the file in the column “employeeemailaddress” exists as a proxy address on at least one user. If not it writes out the e-mail to a log in c:\temp. Nothing too advanced, just a few things put together to achieve a very, VERY tedious task when you get a list of 10.000 e-mail addresses!

This can also be modified to check if any other attribute exists or not on users if you want to, it was just for this scenario that I had to check e-mail addresses! It can also be modified to read out the local AD and not the Azure AD, ofcourse.

Please comment out the first two lines if you run this more than once in a Powershell window since the list of users is already in the variable and reading out all MSOLUsers can take a very long time!

connect-msolservice 
$allusers = Get-MsolUser -All

#Prepping the logg
$DateStamp = Get-Date -Format "yyyy-MM-dd-HH-mm"
$LogFile = ("C:\temp\invalid_emailaddresses-" + $DateStamp + ".log")
# Defining the log function
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}

$csv = import-csv C:\temp\emailaddresses.csv
foreach($csvobject in $csv)
{
    $emailuser = ""
    $emailaddress = $csvobject.employeeemailaddress
    Write-Host -ForegroundColor Yellow "Looking up user with e-mail $emailaddress"
    $emailuser = $allusers | where {$_.proxyaddresses -like "*$emailaddress*"} | select DisplayName
    if(!$emailuser.displayname)
    {
        LogWrite ("Could not find user with e-mail address $emailaddress")
        write-host -ForegroundColor Red "Could not find user with e-mail address $emailaddress"
    }
    else
    {
        write-host -ForegroundColor Green "User found, e-mail address is good"
    }
}

Enable Versioning On Entire SharePoint 2013 Application

SCENARIO
For some reason, probably money, you can’t use a proper backup solution for your farm. So you want to use versioning as a cheap mans backup.

PROBLEM
Going through every document library in every site in every site collection in every application to enable versioning isn’t possible. And there is no way to specify in Central Administration or declare a policy to enforce this.

SOLUTION
This powershell script will do the trick for you. It’s written to enabling versioning for an entire web application (with easy alteration it can be scoped to a specific site/site collection). What’s neat about this is that it will not change settings on the document libraries that already have it enabled! It will not enable minor versioning, but you can just enable that if you want.

As always, use on your own risk and test in a test environment first and then scope it to a test site collection in production farm!!

Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue
$webapp = "ENTER URL TO WEB APPLICATION"
$site = get-spsite -Limit All -WebApplication $WebApp
foreach($web in $site.AllWebs)
{
    Write-Host "Inspecting " $web.Title
    foreach ($list in $web.Lists)
    {
        if($list.BaseType -eq "DocumentLibrary")
        {
            $liburl = $webapp + $list.DefaultViewUrl
            Write-Host "Library: " $liburl
            Write-Host "Versioning enabled: " $list.EnableVersioning
            Write-Host "MinorVersioning Enabled: " $list.EnableMinorVersions
            Write-Host "EnableModeration: " $list.EnableModeration
            Write-Host "Major Versions: " $list.MajorVersionLimit
            Write-Host "Minor Versions: " $list.MajorWithMinorVersionsLimit
            $host.UI.WriteLine()
            if(!$list.EnableVersioning)
            {
                $list.EnableVersioning = $true
                $list.EnableMinorVersions = $false     # Set this to true if you want to enable minor versioning
                #$list.MajorVersionLimit = 10          # Remove comment hashtag and set this to the max amount of major versions you want
                #$list.MajorWithMinorVersionsLimit = 5 # Remove comment hashtag and set this to the max amount of minor versions you want
                $list.Update()
            }
        }
    }
}

Credit goes to Amrita Talreja @ HCL for this post which is the basis for this Powershell script.

Remove User Mailbox Permissions

SCENARIO
A user has alot of mailbox permissions to other mailboxes that needs to be revoked.

PROBLEM
The problem is that the GUI, even in an on-prem interface, forces you to remove the permissions on the destination so you have to go to every mailbox he/she has access to, remove the permission and then go to the next. This is very time consuming, one wish you could open the user and remove the permissions to other mailboxes that way, but it doesn’t work like that unfortunately.

SOLUTION
This little script solves this problem. It goes through all mailboxes in your mailenvironment and checks all the boxes that the [USER] has access to and prompts to remove them one by one. I still feel a prompt is necessary because sometimes you get the request to “remove everything except these”, so by prompting we can chose which ones to remove. Alot faster than going to every mailbox in the list! For better performance, I suggest you specify a “-servername EXCHANGESERVER” in the “get-mailbox” command, otherwise it’ll go through the entire Exchange org.

$user = "[USER]"
$permissions = Get-Mailbox -resultsize unlimited | Get-MailboxPermission -User $user
foreach($permission in $permissions)
{
$identity = $permission.identity
$accessright = $permission.accessrights
write-host "Removing permission for $user on $identity"
remove-mailboxpermission -Identity $identity -User $user -Accessrights $accessright
}

Download PS1 from Dropbox

Download PS1 from Dropbox

Change Something On Users From File

SCENARIO
You’re the administrator of an Office 365 tenant and/or on-prem Exchange and Active Directory and you need to make bulk changes to a group of people and you have a list with UPNs ready to use.

PROBLEM
There really is no problem but it may be very repetetive tasks.

SOLUTION
This script will read the file “C:\temp\list_of_upns.txt“, which is just a list of UPN’s, and iterate through them making the change you want. Since I make alot of the same changes to different users depending on what I need. I simply un-comment by removing the “#” for whatever I need to script to change on the user. And remember to put the “#” back in to comment if you want it to do something else or you may end up doing unwanted things on the objects (like converting a bunch of on-prem mailboxes to Room mailboxes, true story!)

# Written by : Kristoffer Strom ([email protected])
# Date: 2017-02-20
#
#Starting the loop
ForEach ($user in $(Get-Content C:\temp\list_of_upns.txt ))
{
write-host $user
#Set-Mailbox $user -Type shared
#$userdn = get-aduser  -Filter{UserPrincipalName -eq $user} -properties DistinguishedName
#$DN = Get-ADUser -Filter { UserPrincipalName -Eq $user }
#set-aduser $DN -Replace @{extensionAttribute1 = "REPLACEMENTTEXT"}
#set-aduser $DN -clear extensionAttribute1, extensionAttribute1, AdminDisplayname, AdminDescription
#remove-adgroupmember -Identity "ADGROUP" -Member $DN
#Set-MsolUserLicense -UserPrincipalName $user -RemoveLicenses "XXXXXXXXXX:ENTERPRISEPACK"
#Set-MsolUserLicense -UserPrincipalName $user -RemoveLicenses "XXXXXXXXXX:POWER_BI_STANDARD"
}

Download PS1 from Dropbox

Download PS1 from Dropbox

Get User Serviceplans

SCENARIO
You’re the administrator of Office 365 and you want to programmatically extract information about what serviceplans (features) a specific user has access to and which he or she doesn’t have access to.

PROBLEM
Microsoft’s way of storing the information regarding licenses and features/plans isn’t quite logical sometimes for unitiated people so this might sound like a very complicated thing to do with Powershell and you’re left to do it through the portal instead.

SOLUTION
This little snippet of code will help you. You can add exporting features to it if you want, or input a user.txt file, but this is the stuff that displays the information. But if you want to get a complete dump of all information for all users, you should use this script instead which does that magic alot better.

#
# Written by : Kristoffer Strom ([email protected])
# Date: 2017-02-20
#
# Here we define what user we're querying:
$upn = "[INSERT UPN HERE]"
# Here we set the index to 0
$i = 0
# Run the query to get user info
$user = get-msoluser -userprincipalname $upn
# Store the license array
$features = $user.licenses
# Just an empty row
write-host ""
# Running the loop to show info on all licenses
while($i -lt $features.count)
{
    $AccountSkuId = $features[$i].AccountSkuId
    Write-host -NoNewline -ForegroundColor Cyan "Features for $AccountSkuId"
    $features[$i].servicestatus | ft
    $i++
}

Download PS1 from Dropbox

Download PS1 from Dropbox

UPDATE: Apparently the new Powershell moduled for MSOL handled this differently so the output became quite different. Rather than type out one plan at a time it bunched them together so there was no way of seeing which feature belonged to which plan. So I re-wrote this with a “while” loop and “format table” command to force it to seperate the output. (seriously, remove the “ | ft” part and see what that does to this snippet!)

Get AD User By UPN

SCENARIO
You’re managing Office 365 and want to do Powershell quieries against the on-premise Active Directory

PROBLEM
The problem is that Office 365 cmdlets like “get-msoluser” always gives you the users userprincipalname (UPN) because that is all that Office 365 cares about. But when you want to query the local on-premise Active Directory with “get-aduser” it doesn’t recognise the UPN when searching for users.

SOLUTION
The solution is adding it as a filter like this, where $MSOLUPN is the UPN you get from “get-msoluser“:

Get-ADUser -Filter { UserPrincipalName -Eq $MSOLUPN }

Again, this is pretty basic stuff, but still something you need to know and use all the time.

Set UsageLocation Where Empty

SCENARIO
You’re the administrator of a large tenant with several different e-mail domains for people in different countries. Manually setting a UsageLocation, which is required for license activation, for them all individually is unrealistic.

PROBLEM
The problem here is we need some way of filtering out who is actually where. The perfect solution is ofcourse to have the AD property (“msExchUsageLocation”) since Azure AD Connect syncs it out of the box. But not everyone has that luxury, especially if it’s a domain you’re not even in charge of. And the get-msoluser cmdlet with -domain filter will not only catch the ones that have the domain as their primary e-mail, but also the ones that have it as secondary so as a filter it won’t work.

SOLUTION
In comes this little bit of code! It loads all users, filters out everyone that has a license and everyone that doesn’t have a UPN matching the domain (in this example “test.se” and for the rest sets the UsageLocation to whatever you want, in this example “SE“. This way, we only get the ones that don’t have a license yet and only the ones with this specific domain as their primary e-mail (which should match UPN). So if your company’s name is “test” and everyone has a “test.com” address as a secondary, but the Swedish employees have “test.se” as their primary you can easily set their usagelocations like this. And then just change it around for the rest of the company.

#
# Written by : Kristoffer Strom ([email protected])
# Date: 2017-02-10
#
# Credit to Reditor bearxor (https://www.reddit.com/user/bearxor)
#
# We check all users for users with no UsageLocation set and matches a e-mail domain (test.se) and sets the UsageLocation SE (=Sweden)
Get-MsolUser -All | where{$_.UsageLocation -eq $null -and $_.userprincipalname -like "*@test.se"} | foreach{set-msoluser -UserPrincipalName $_.UserPrincipalName -UsageLocation "SE"}

Download PS1 from Dropbox

Download PS1 from Dropbox

Add Users To Group Based On Attribute

SCENARIO
You have a bunch of users with some property in common in your on-prem AD and you want to add them all to an AD group.

PROBLEM
Normally to add a bunch of users to an AD group you’d simple use “dynamic groups” and set the filter up and never bother with it again. This however becomes a problem when dealing with Azure AD since that doesn’t support dynamic groups (since it can’t look up all properties in your AD).

SOLUTION
The solution is below. You specify the name of the AD group (“NAME_OF_AD_GROUP” in example below) you want to add users, you change what attribute (“extensionattribute1“) you want it to filter on and what the attribute has to be (“WHATEVER“) and the script iterates through every users and adds them to the AD group. This can be setup as a secheduled task to get the same effect as a dynamic group but would work for Azure AD as well.

#
# Written by : Kristoffer Strom ([email protected])
# Date: 2017-02-09
#
# We start by defining what AD group we want to add the users to
$nameofgroup = "NAME_OF_AD_GROUP"
# Then we get a list of users based on an attribute, in this case "Extensionattribute1" equals "WHATEVER"
$listofusers = Get-ADUser -filter {extensionattribute1 -eq "WHATEVER"}
# Now we iterate through every user
foreach($individualuser in $listofusers)
  {
  # We declare this variable for write-host purposes only.
  $UserPN = $individualuser.UserPrincipalName
  # And we can write out the operation while we're at it
  Write-Host "Adding $UserPN to group $nameofgroup."
  # And add them to the group
  Add-ADGroupMember $nameofgroup $individualuser.DistinguishedName
  }

Download PS1 from Dropbox

Download PS1 from Dropbox

Bulk Converting Domains To Federated

SCENARIO
You’re the administrator of an Exchange environment with lots of domains registered over the years for whatever reasons, as an example different business units with different e-mail domains. You’ve added them all to the Azure AD and verified them but now you need to tie them to the AD Federation Service (ADFS).

PROBLEM
Problem is it takes alot of time to first sort out all domains that are verified and then federating them, a very tedious task.

SOLUTION
Solution is you export all your domains into a CSV file (just listing all the domainnames is fine), the run this script and it will import the CSV file and for every entry it will check to make sure if it’s verified and if so, federate it with the ADFS. Remember to run this on the ADFS server and the Powershell needs to be launched as administrator!

#
# Written by : Kristoffer Strom ([email protected])
# Date: 2017-02-08
#
# Let's begin by importing the file. Change the filename "CSV_FILENAME.csv" to whatever you see fit.
$domains = Import-Csv CSV_FILENAME.csv
# And now we iterate through every entry
foreach ($domain in $domains)
{
  # Getting the status of the domain
  $domainstatus = get-msoldomain -DomainName $domain.DomainName
  # If it's already federated we just say that and move onto the next one
  if($domainstatus.Authentication -eq "Federated") { write-host -Foregroundcolor Yellow "$domain is already federated." }
    # If it's verified we federated it
    ElseIf($domainstatus.Status -eq "Verified") { Convert-MsolDomainToFederated -DomainName $domain.DomainName -SupportMultipleDomain:$true; write-host -Foregroundcolor Green "$domain.DomainName changed to federated" }
    # Or if it's not Verified or doesn't exist we write this error
    ElseIf($domainstatus.Status -ne "Verified") { write-host -Foregroundcolor Red "$domain is not verified or does not exist in tenant." }
}
# End of iteration

OPTIONAL
You could replace the import of the CSV file to read out all the UPN suffixes from your domain. If you’ve done your job for a proper O365 migration you’ve made sure all the UPN’s match their e-mails then all e-mail domains should exist as a UPN suffix. If you want to do that, replace the line “$domains = Import-Csv CSV_FILENAME.csv” with this:

$ADForest = Get-ADForest
$domains = $ADForest.UPNSuffixes

Another option is to do a get-msoldomain and filter on “Verified” domains only. But beware, this will tie all verified domains to your ADFS, be sure you really want that! If you do, replace the “$domains=” statement with this:

$domains = Get-MsolDomain -Status Verified

This script can easily be converted into one that does the initial adding of the domains, but since every domain added gets a vertification code backs doing that in bulk is less than ideal.

Download PS1 from Dropbox

Download PS1 from Dropbox

Powershell to Exchange Online

SCENARIO
You’re used to having your Exchange server in your own environment and Powershelling to it and run all these scripts you’ve collected over the years.

PROBLEM
Now that the mailboxes are in a database you have no control of on a server somewhere at Microsoft how are you supposed to run those Exchange Powershells?

SOLUTION
This is the MS way of executing Powershell against Exchange Online using Powershell commands you’d usually use on an on-premise Exchange environment. This is very basic stuff, but if you’re just getting started with Office 365, this is essential to manage users in Exchange Online!

$UserCredential = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session

Basically what this does is connects you to Exchange Online and starts a remote Powershell session on Microsoft’s server. This unfortunately means that you are limited to the automation limits, which means sometimes when you do really, really heavy stuff you’ll run into throttling errors from “Microsoft.Online.Administration.Automation.MicrosoftOnlineException”. Usually that’s just an error saying you need to code better with more filters!