Friday, February 19, 2021

Powershell and Uptimerobot

Uptimerobot can be quite tedious when you need to update many monitors at once.
For example say you bought the license for Uptimerobot and now you want to change 50 monitors to check status every 60 seconds instead of the free modes 300 second interval. 

Luckily Uptimerobot have a great API that we can work with powershell against.

getmonitors.ps1 is method to export all monitors to local file. For easier import to another account, or just a small backup just in case. All monitors on account is exported to file note.txt. Using https://api.uptimerobot.com/v2/newMonitor/getMonitors

addmonitors.ps1 is example of how to add new monitor using https://api.uptimerobot.com/v2/newMonitor

editMonitors.ps1  is method for updating all monitors in account to use 60seconds interval.
Using
https://api.uptimerobot.com/v2/editMonitor


$APIKeyWrite = <apikey>
$APIUrl = "https://api.uptimerobot.com/v2/"
$listMonitors = "newMonitor"
$urlRequest = $APIUrl+$listMonitors
$contentType = "application/x-www-form-urlencoded"
$headers = @{"cache-control"="no-cache"}
$params = @{"api_key"=$APIKeyWrite;
"format"="json"
"type"="2"
"url"="https://www.example.com"
"friendly_name"=MyWeb"
"keyword_type"="2"
"keyword_value"="Contact Us"}
#type1=http,type1=kywd
$contentWrite = (Invoke-WebRequest -Uri $urlRequest -Method POST -Headers $headers -body $params).content |convertFrom-json
view raw addMonitors.ps1 hosted with ❤ by GitHub
$APIKeyWrite = <apikey>
$newInterval = 60
$APIUrl = "https://api.uptimerobot.com/v2"
$contentType = "application/x-www-form-urlencoded"
$headers = @{"cache-control"="no-cache"}
$params = @{"api_key"=$APIKeyWrite;"format"="json";"logs"="1"}
$content = (Invoke-WebRequest -Uri "$($APIUrl)/getMonitors" -Method POST -Headers $headers -body $params).content |convertFrom-json
#foreach ($monitor in $($content.monitors |select -first 10)) {
foreach ($monitor in $content.monitors) {
Remove-Variable itemparams,content -ErrorAction SilentlyContinue
if ($monitor.interval -ne $newInterval) {
write-output "Monitor interval before: $($monitor.interval)"
$itemparams = @{"api_key"=$APIKeyWrite
"format"="json"
"id"=$monitor.id
"interval"=$newInterval}
$monitor
$content = (Invoke-WebRequest -Uri "$($APIUrl)/editMonitor" -Method POST -Headers $headers -body $itemparams).content|ConvertFrom-Json
if ($content.stat -like "fail") {
write-warning "Failed, error: $($content.error)"
}
elseif ($content.stat -like "ok") {
write-output "Updated $($monitor.friendly_name) ok to $newInterval"
}
}
else {
write-output "$($monitor.friendly_name) didn't need to update. Skipping..."
}
}
$APIKeyReadOnly = <apikey>
$scriptPath = $(split-path $MyInvocation.MyCommand.Definition -Parent)
$APIUrl = "https://api.uptimerobot.com/v2/"
$listMonitors = "getMonitors"
$urlRequest = $APIUrl+$listMonitors
$contentType = "application/x-www-form-urlencoded"
$headers = @{"cache-control"="no-cache"}
$params = @{"api_key"=$APIKeyReadOnly;"format"="json";"logs"="1"}
$content = (Invoke-WebRequest -Uri $urlRequest -Method POST -Headers $headers -body $params).content |convertFrom-json
$content.monitors |select url, status,keyword_value |out-file -filepath "$($scriptpath)\note.txt"
view raw getMonitors.ps1 hosted with ❤ by GitHub

Friday, November 27, 2020

Sharepoint 2019 and mysites

 I had some problems with the old familiar "Working on it" for mysites in Sharepoint 2019. It was difficult to find information about this, Im guessing the installation base for world wide on-premise installations is dropping.

Key information when troubleshooting

- On User Profile, attribute "Personal Site Capabilities" is set after user visits mysite for the first time. Default behaviour seems to be setting this to 4. Meaning only storage.
- To reset user mysite, remove the mysite and attributes for mysite and Personal Site Capabilites are automaticly removed from User Profile
- To check ULSViewer for Personal Site Capabilites settings, filter for eventid =  aj1lz
- FeedServiceIdentifier is based on Microfeeds-list on user mysite, without site feature MySiteSocialDeployment no microfeeds is created.
- User Profile Service Application_ActivityFeedJob is running at 10min interval.
- To track mysite creating in ULSViewer, filter on Category = Personal Site Instantation

To solve the problem

After verifying all the user profile service properties and mysite settings I ended up with what might be a bug or might be design. The MySiteSocialDeployment seems to be missing on the default mysite template. So using the solution from Trevors site below i built some scripts for easier deployment in case Microsoft updates onet.xml i future updates.

So basicly first update onet.xml configuration site with ID 10 to include the missing MySiteSocialDeployment-feature
Then deploy the updated file to all members with copyupdatefile.ps1
Finally update sitemaster with newsitemaster.ps1
Remove previously created mysites to trigger mysite-creation with correct features.

$location = "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\TEMPLATE\SiteTemplates\SPSPERS\XML\onet.xml"
$scriptpath = $(split-path $MyInvocation.MyCommand.Definition -Parent)
#backupfile function
$backupFolder = "$scriptpath\backup\original"
$changedFolder = "$scriptpath\backup"
if ((Test-Path -PathType Container -Path $backupFolder) -eq $false) { New-Item -ItemType Directory -Force -Path $backupFolder |Out-Null }
Copy-Item -Path $location -Destination $backupFolder
Copy-Item -Path $location -Destination $changedFolder
$changedFile_disk = "$($changedFolder)\onet.xml"
#edit file if value doesn't exists function
[xml]$xml = get-content -Path $changedFile_disk
#check if exists
$nodeExists = $xml.selectSingleNode('//Configuration[@ID="10"]/SiteFeatures/Feature[@ID="b2741073-a92b-4836-b1d8-d5e9d73679bb"]')
if ($nodeExists) {
write-output "Node Already exists"
}
else {
try {
write-output "node doesn't exists. Adding..."
$parentNode = $xml.SelectSingleNode('//Configuration[@ID="10"]/SiteFeatures')
$newNode = $parentNode.FirstChild.clone()
$newNode.id = "b2741073-a92b-4836-b1d8-d5e9d73679bb"
$parentNode.AppendChild($newNode)
$xml.save($changedFile_disk)
}
catch {write-output "Something went wrong: $($_.exception)"}
}
$servers = "\\<myserver>"
$destination = "c$\Program Files\Common Files\microsoft shared\Web Server Extensions\16\TEMPLATE\SiteTemplates\SPSPERS\XML"
$updateFile = "$($scriptpath)\backup\onet.xml"
foreach ($server in $servers) {
$currentdestination = join-path -path $server -childPath $destination
write-output "Copying $updateFile to $currentDestination"
copy-item -Path $updateFile -Destination $currentdestination
}
Add-PSSnapin microsoft.sharepoint.powershell
$mysiteUrl = <https://mysite.mycompany.com>
$database = Get-SPContentDatabase -WebApplication $mysiteUrl
$master = Get-SPSiteMaster -ContentDatabase $database
Remove-SPSiteMaster -SiteId $master.SiteId -ContentDatabase $database -confirm:$false
New-SPSiteMaster -Template "SPSPERS#10" -ContentDatabase $database
#verify after
$master = Get-SPSiteMaster -ContentDatabase $database
$master.FeaturesToActivateOnCopy | ft -a


References:
Details about PersonalSiteCapabilites
the fix from Trevor
Good details about Fast Site Creating
Another site with same fix
German site using the same fix

Monday, November 02, 2020

Handle multiple versions of powershellPnp with Sharepoint

 This is a nice way to handle accessing different versions of PNPPowershell. 
I use this when switching between onpremise environments of different version and online.

Note that env-variables can't be used until session, like ISE is restarted. Otherwise can variable be accessed immediatly using 
[System.Environment]::GetEnvironmentVariable('SharePointPnPPowerShell2019','machine')

setupEnv.ps1 downloads named modules in $array to $saveFolder. 
Then locates psd1-file and adds this as a enviroment variable with name in arrayobject. 
To use the module access env-variable with $env:name as showed in importModule_Example.ps1

import-module $env:SharePointPnPPowerShell2019 -DisableNameChecking
Remove-Module -Name SharePointPnPPowerShell2019
Import-Module $env:sharepointpnppowershell2016 -DisableNameChecking
Remove-Module -Name SharePointPnPPowerShell2016
Import-Module $env:SharePointPnPPowerShellOnline -DisableNameChecking
Remove-Module -Name SharePointPnPPowerShellOnline
$saveFolder = "C:\pnpPowershell"
$array = @()
$array += [pscustomobject]@{Name="sharepointpnppowershell2019";path=""}
$array += [pscustomobject]@{Name="SharePointPnPPowerShellOnline";path=""}
$array += [pscustomobject]@{Name="sharepointpnppowershell2016";path=""}
if ((test-path -PathType Container -Path $saveFolder) -eq $false) {New-Item -ItemType Directory -Force -Path $saveFolder |out-null}
foreach ($item in $array) {
save-module -name $($item.name) -path $saveFolder -Force
}
foreach ($item in $array) {
$item.path = Get-ChildItem -Path $saveFolder -Recurse |where-object {$_.Name -like "$($item.Name).psd1"} |select -ExpandProperty FullName
if (![string]::IsNullOrEmpty($item.path)) {
[System.Environment]::SetEnvironmentVariable($item.name, $item.path,[System.EnvironmentVariableTarget]::Machine)
}
}
view raw setupEnv.ps1 hosted with ❤ by GitHub


References:
https://www.erwinmcm.com/running-the-various-versions-of-pnp-powershell-side-by-side/



Tuesday, June 02, 2020

Converting csv to excel

Using the useful powershell module importExcel
I could use this to import folder with csv-files to a excelfil for more easy viewing for common users.
Use commented install-module line for first run.


#Install-Module ImportExcel -scope CurrentUser
Import-Module importexcel
$inputFilePath = "C:\temp\csvFiles"
$outputFilePath = "C:\temp\csvFiles\myExcel.xlsx"
$files = get-childitem -Path $inputFilePath -Filter "*.csv" #|select -First 1
if (Test-Path $outputFilePath) { Remove-Item -Path $outputFilePath -Force }
foreach ($file in $files) {
#for files with naming standard myfile_sheetname.csv
$sheetName = $file.name.split("_")[-1].split(".")[0]
write-output "exporting $($file.name)..."
$collection = import-csv -Path $file.FullName
$collection |export-excel -Path $outputFilePath -WorksheetName $sheetName -AutoSize -TableName $sheetName
}
References: https://github.com/dfinke/ImportExcel

Tuesday, April 21, 2020

Using powershell with UptimeRobots API

I got a chance to test Uptimerobots API with Powershell and I used it with a csv-list of urls with keywords to update my uptimerobot account.
Pretty good stuff where you also can output all your monitors with uptimelogs directly to PowerBI or equivalent.

Code:
$APIKeyWrite = "<insertYourWriteAPIKeyHere" #API Write Key from uptimerobot
$scriptPath = split-path $MyInvocation.MyCommand.Definition -Parent
$csvPath = "$($scriptPath)\MonitorList.csv"
$monitorList = import-csv -Path $csvPath -Delimiter ";"
$APIUrl = "https://api.uptimerobot.com/v2/"
$contentType = "application/x-www-form-urlencoded"
$headers = @{"cache-control"="no-cache"}
$getMonitorParams = @{"api_key"=$APIKeyWrite;"format"="json";}
$existingMonitors = ((Invoke-WebRequest -Uri $($apiUrl+"getMonitors") -Method POST -Headers $headers -body $getMonitorParams).content |convertFrom-json)|select -ExpandProperty monitors |select friendly_name,url,keyword_value,id
foreach ($monitor in $monitorList) {
Remove-Variable currentMonitor -ErrorAction SilentlyContinue
$currentMonitor = $existingMonitors | where-object {$_.url -like "https://$($monitor.url)"}
if ($existingMOnitors.url -notcontains "https://$($monitor.url)") {
write-output "Doesn't exists, creating $($monitor.url)..."
$params = @{"api_key"=$APIKeyWrite;"format"="json";"type"="2";"url"="https://$($monitor.url)";"friendly_name"="$($monitor.friendlyName)";"keyword_type"="2";"keyword_value"="$($monitor.keyword)"}
$contentWrite = (Invoke-WebRequest -Uri $($apiUrl+"newMonitor") -Method POST -Headers $headers -body $params).content |convertFrom-json
$contentWrite
}
else {
if ($currentMonitor.keyword_value -notlike $monitor.Keyword) {
write-warning "$($monitor.url): Updating keyword..."
$editParams = @{"api_key"=$APIKeyWrite;"format"="json";"type"="2";"id"=$currentMonitor.id;"keyword_value"="$($monitor.keyword)"}
$contentWrite = (Invoke-WebRequest -Uri $($apiUrl+"editMonitor") -Method POST -Headers $headers -body $editParams).content |convertFrom-json
$contentWrite
}
else {
write-output "$($monitor.url) settings is matched. Skipping..."
}
}
}
We can make this file beautiful and searchable if this error is corrected: No commas found in this CSV file in line 0.
url;friendlyName;Keyword
site1.company.com;site1;Hello
site2.company.com;site2;Hello
view raw monitorList.csv hosted with ❤ by GitHub


To get UptimeRobot status from PowerBI the following queries can be used:

Example 1 - Simple query

let
   body = Text.ToBinary("api_key=#myAPIKey#&format=json&logs=1"),
   Options = [
   Headers=[#"Content-type"="application/x-www-form-urlencoded", #"cache-control"="no-cache"],
   Content=body
   ],
   result = Web.Contents(url, Options)
in
    result


Example 2 - Query using records


let
   content= [
      #"api_key"="#myAPIKey#",
      #"format" = "json",
      #"logs" = "1"
   ],
   query = Text.ToBinary(Uri.BuildQueryString(content)),
   Options = [
   Headers=[
      #"Content-type"="application/x-www-form-urlencoded",
      #"cache-control"="no-cache"],
      Content=query
   ],
   result = Web.Contents(Url, Options)
in
    result
 
Example 3 - Query using json
 
let
   content = "{
  ""api_key"": ""#myAPIKey#"",
  ""format"": ""json"",
      ""logs"": ""1""        
    }",
   query = Text.ToBinary(Uri.BuildQueryString(Json.Document(content))),
   Options = [
   Headers=[
      #"Content-type"="application/x-www-form-urlencoded",
      #"cache-control"="no-cache"],
      Content=query
   ],
   result = Web.Contents(Url, Options)  
in
    result

Tuesday, April 14, 2020

SelfService sitecollections in Sharepoint 2019

With Sharepoint 2019 we now have the ability to allow users to create site collections outside their own mysite themselves. This allows for less administration.
The drawback of this solution is that you can only create managed path sites on primary Alternate Access for webapplication. 
Microsofts documentation leaves some holes in regards to how to activate selfservicing functions with powershell in Sharepoint 2019 so I digged for the related values and found settings below. Most are obvious, one wasn't.


Script below
Add-PSSnapin microsoft.sharepoint.powershell
$WebApp = Get-SPWebApplication "https://mysite.company.com/"  
$webApp.ShowStartASiteMenuItem = $false
$webApp.SelfServiceSiteCreationEnabled = $false  
$webApp.Update()  
Add-PSSnapin microsoft.sharepoint.powershell
$WebApp = Get-SPWebApplication "https://mysite.company.com/"  
$webApp.SelfServiceSiteCreationEnabled = $true  
$webApp.ShowStartASiteMenuItem = $true
$webApp.SelfServiceCreationAlternateUrl = "https://workspace.company.com"
$webApp.Update()  




References:
https://docs.microsoft.com/en-us/sharepoint/sites/configure-self-service-site-creation-in-sharepoint-server-2019

Friday, January 31, 2020

Blobcache issues

I had some weird problem with images in Sharepoint 2013.
Issues were for example
  • Updated images didn't work, old images still showed for clients. 
  • Change imageproperties didn't work either
  • Changing website logo didn't work
BlobCache quickly became as suspect, so we tried to flush the blobcache the proper way with flushBlobCache.ps1 but the folders in the blobcache-directory didn't get recreated.
Expected behaviour is when flushblob has run on farm, the folder under siteID in blob-directory will get a new created date and new name.
For me, they just left the old folder without change on certain farm-members.

After searching for a official solution I landed on my own fix which was as follows
  1. Redirect trafik from webfrontend or do this during servicewindow
  2. Update url and run setBlobDisabled.ps1 (or disable blob manually in web.config)
  3. This will cause a reset to Application Pool and blobfolder will stop updating change.bin file in blob temp-directory
  4. Rename or delete folder with SiteID, for example E:\Blob\14\354006434 to 354006434_old
  5.  Update url and run setBlobEnabled.ps1
  6. Problem solved.


 
Add-PSSnapin Microsoft.SharePoint.Powershell -ErrorAction SilentlyContinue
$WebApplication = Get-SPWebApplication <my web application url>
[Microsoft.SharePoint.Publishing.PublishingCache]::FlushBlobCache($WebApplication)
Write-Host "Flushed the BLOB cache for:" $WebApplication
#disable blob cache
$scriptpath = $(split-path -Parent $MyInvocation.MyCommand.Definition)
$webConfigPath = <my web folder>\web.config"
#save backup
copy-item -Path $webConfigPath -Destination "$($scriptPath)\backup\$(get-date -format 'yyMMdd_HHmm')-web.config"
#make change
[xml]$webConfig = Get-Content $webConfigPath -ReadCount 1
$node = $webConfig.SelectSingleNode('//BlobCache')
if ($node.enabled -notlike "false") {
$node.enabled = "false"
$webConfig.Save($webConfigPath)
write-output "Blobcache disabled"
}
else {
write-warning "Value already set"
}
#disable blob cache
$scriptpath = $(split-path -Parent $MyInvocation.MyCommand.Definition)
$webConfigPath = <my web folder>\web.config"
#save backup
copy-item -Path $webConfigPath -Destination "$($scriptPath)\backup\$(get-date -format 'yyMMdd_HHmm')-web.config"
#make change
[xml]$webConfig = Get-Content $webConfigPath -ReadCount 1
$node = $webConfig.SelectSingleNode('//BlobCache')
if ($node.enabled -notlike "true") {
$node.enabled = "true"
$webConfig.Save($webConfigPath)
write-output "BlobCache enabled"
}
else {
write-warning "Value already set"
}

Powershell and Uptimerobot

Uptimerobot can be quite tedious when you need to update many monitors at once. For example say you bought the license for Uptimerobot and n...