Category Archives: Powershell

Deploy MFA to all Administrator accounts in all (Partner) tenants

As a MSP we tend to take over a lot of Microsoft tenants which therefore do not have their state of security in order. To make sure that we always use MFA for administration purposes we have an Azure Function running that deploys MFA for our administrator accounts. We do this by using our central phone number. That way MFA is always configured and we notice when an admin is trying to log in. We tend to use our delegated access as the normal administration purpose but some tasks require a local admin account in the customer’s portal.

We currently have the following running as an Azure Function. I’ve edited the script to allow anyone to run this directly. If you need help converting this to an Azure Function please see my other blog here.

The great thing about this script is that we completely negate the need for our helpdesk to configure MFA for the admin accounts. The script makes sure that MFA is completely prepared for them all.

Script breakdown

In the first part of the script we set our phone numbers, connect to the MSOLService and get all the Microsoft Partner Contracts we have permissions on

$MobileNumber = "+31611111111"
$AltNumber = "+31010101010"
Connect-MsolService
$ClientList = get-msolpartnerContract -All

Now that $ClientList contains all our clients we loop trough these to get all of our admin accounts.

foreach($Client in $ClientList){
$adminemails = Get-MsolRoleMember -TenantId $Client.tenantid -RoleObjectId (Get-MsolRole -RoleName "Company Administrator").ObjectId
$admins = $adminemails | get-msoluser -TenantId $client.tenantid

Now that we have our admin accounts in $Admins the next step is to create two objects – One for the Strong Authentication Requirements and another for the Strong Authentication Method.

$AuthReq = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
$AuthReq.RelyingParty = "*"
$AuthReq.State = "Enabled"
$AuthReq.RememberDevicesNotIssuedBefore = (Get-Date)
$AuthMethod = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationMethod
$AuthMethod.MethodType = "TwoWayVoiceMobile"
$AuthMethod.IsDefault = $true
$AuthMethodObj = @($AuthMethod)

This makes sure that the user is completely pre-provisioned and does not need to go through the entire process of activating MFA first, which saves us valuable time. To finalize we run a for-each loop which enables all found Admin accounts with Multi Factor Authentication. It also sets the phone numbers to the ones we’ve entered as variables above.

foreach ($admin in $admins | Where-Object {$_.StrongAuthenticationMethods -eq $Null}) {
        Write-Host "Enabling MFA for $($admin.userprincipalname)" -ForegroundColor Green
        Set-MsolUser -UserPrincipalName $admin.userprincipalname -StrongAuthenticationRequirements $AuthReq -StrongAuthenticationMethods $AuthMethodObj -TenantId $admin.tenantid  -MobilePhone $MobileNumber -AlternateMobilePhones $AltNumber
    }
}

And thats it! as a result the clients Secure Score will increase by about 50 points. Awesome! The complete script will look like this

Final Script

$MobileNumber = "+31611111111"
$AltNumber = "+31010101010"
Connect-MsolService
$ClientList = get-msolpartnerContract -All

foreach($Client in $ClientList){
$adminemails = Get-MsolRoleMember -TenantId $Client.tenantid -RoleObjectId (Get-MsolRole -RoleName "Company Administrator").ObjectId
$admins = $adminemails | get-msoluser -TenantId $client.tenantid
$AuthReq = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
$AuthReq.RelyingParty = "*"
$AuthReq.State = "Enabled"
$AuthReq.RememberDevicesNotIssuedBefore = (Get-Date)
$AuthMethod = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationMethod
$AuthMethod.MethodType = "TwoWayVoiceMobile"
$AuthMethod.IsDefault = $true
$AuthMethodObj = @($AuthMethod)
foreach ($admin in $admins | Where-Object {$_.StrongAuthenticationMethods -eq $Null}) {
        Write-Host "Enabling MFA for $($admin.userprincipalname)" -ForegroundColor Green
        Set-MsolUser -UserPrincipalName $admin.userprincipalname -StrongAuthenticationRequirements $AuthReq -StrongAuthenticationMethods $AuthMethodObj -TenantId $admin.tenantid  -MobilePhone $MobileNumber -AlternateMobilePhones $AltNumber
    }
}

Tada! as always, be careful, and happy PowerShelling!

Connecting to all O365 services at the same time in PowerShell (Including Installation and Teams)

I often use scripts that uses cmdlets from different modules so I can use all sorts of data sets when handling my Office365 administration tasks. The problem is that I often found myself connecting to a specific service such as the Exchange services, only to need the security center moments later – or the MSOL Module right after. When reinstalling my laptop I decided to not bump into this anymore, mostly to just get rid of my annoyance.

After a short search online I’ve found that there already was a bit of an example here on the Microsoft docs website. Unfortunately this guide is outdated and does not included the newer Teams Module. It also does not automatically find the correct SharePoint URL. As I am quite lazy and never want to look things up I also added the functionality to find the correct URL and to download the right modules. Currently it connects to the following services:

Services

ServiceModule Name
AzureAD (Graph)AzureAD Module
AzureAD(MSOL)MSOL Module (Legacy)
TeamsMicrosoftTeams(Beta)
Exchange OnlinePSSession to Exchange Endpoint
Security & CompliancePSSession to Protection endpoint
SharepointMicrosoft.Online.SharePoint.PowerShell
Skype for Business SkypeOnlineConnector

The function has only two options; a -Disconnect in case you want to end the sessions cleanly, and a -Credentials option for passing the credentials. Both are optional. To make sure the Function is always available to me I’ve added it both to my VSCode Profile and my PowerShell profile by starting a PowerShell prompt in both tools and entering the following one-liner:

New-Item -Path $profile -ItemType File

This creates a file in your profile path. Any code you paste here will always be loaded on startup. Because I use a lot of functions I do not like posting my code directly here, instead I save my modules to a specific folder, and have it look in that folder for any functions that I add.

$MyFunctions = "C:\Posh\Functions"
write-host "Loading Functions" -ForegroundColor Yellow
Get-ChildItem "$MyFunctions\*.ps1" | %{.$_}
write-host "Done Loading functions." -ForegroundColor Green 

This small piece of code looks in C:\Posh\Functions for all .PS1 files and loads these files as if I just run them as a script. This makes sure small functions which do not need to be an entire module on their own are loaded correctly. In C:\Posh\Functions I’ve saved the following script which I’ve called “Connect-Office365.ps1”

Script

function Connect-Office365 {
    param(
        [System.Management.Automation.PSCredential]$Credential,
        [switch]$Disconnect
    )
    if($Disconnect){
    Get-PSSession | remove-pssession
    Disconnect-SPOService
    Disconnect-AzureAD 
    exit }
write-host "Checking Prerequisites" -ForegroundColor Green
try{
    Import-Module AzureAD -ErrorAction stop 
} catch { 
        write-host "Could not find AzureAD Module. Installing."  -ForegroundColor Green
        install-module -Name AzureAD -Scope CurrentUser -force
        Import-Module AzureAD -ErrorAction stop 
        }
try{
     Import-Module MSOnline -ErrorAction stop
        } catch { 
     write-host "Could not find MSOL Module. Installing."  -ForegroundColor Green
     install-module -Name MSOnline -Scope CurrentUser -force
     Import-Module MSOnline -ErrorAction stop 
       }

 try{
     Import-Module MicrosoftTeams -ErrorAction stop
    } catch { 
     write-host "Could not find MSTeams Module. Installing"  -ForegroundColor Green
     install-module -Name MicrosoftTeams -Scope CurrentUser -force
     Import-Module MicrosoftTeams
      }
try{
    Import-Module Microsoft.Online.SharePoint.PowerShell -ErrorAction stop
    } catch { 
    write-host "Could not find Sharepoint Module. Installing"  -ForegroundColor Green
    Install-Module -Name Microsoft.Online.SharePoint.PowerShell -Scope CurrentUser -force
    Import-Module Microsoft.Online.SharePoint.PowerShell
    }
try{
    Import-Module SkypeOnlineConnector -ErrorAction stop
    } catch { 
    write-host "Could not find Skype For Business Module. Installing"  -ForegroundColor Green
    Invoke-WebRequest "https://download.microsoft.com/download/2/0/5/2050B39B-4DA5-48E0-B768-583533B42C3B/SkypeOnlinePowerShell.Exe" -OutFile "$($env:TEMP)/SkypeOnlinePowerShell.exe"
    Start-Process "$($env:TEMP)/SkypeOnlinePowerShell.exe" -ArgumentList "/install /quiet" -wait
    Import-Module "C:\Program Files\Common Files\Skype for Business Online\Modules\SkypeOnlineConnector"
     }
if(!$Credential){ $credential = Get-Credential }
write-host "Connecting to AzureAD Module" -ForegroundColor Green
Connect-AzureAD -Credential $credential
write-host "Connecting to MSOL Services" -ForegroundColor Green
Connect-msolservice -Credential $credential
write-host "Connecting to Teams Services" -ForegroundColor Green
Connect-MicrosoftTeams -Credential $credential
write-host "Connecting to Exchange Online" -ForegroundColor Green
$exchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $credential -Authentication "Basic" -AllowRedirection
Import-PSSession $exchangeSession -DisableNameChecking
write-host "Connecting to Security & Compliance Center" -ForegroundColor Green
$SccSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.compliance.protection.outlook.com/powershell-liveid/" -Credential $credential -Authentication "Basic" -AllowRedirection
Import-PSSession $SccSession -Prefix cc
Connect-MicrosoftTeams -Credential $Credential
write-host "Getting initial domain for Sharepoint Online"
$InitDomain = (Get-MsolDomain | Where-Object { $_.IsInitial -eq $true }).name
$InitDomain = $InitDomain.Split(".")
$SharepointDomain = $InitDomain[0]
write-host "Connecting to $($SharePointDomain) Sharepoint"
Connect-SPOService -Url "https://$($SharepointDomain)-admin.sharepoint.com" -credential $Credential
$sfboSession = New-CsOnlineSession -Credential $credential
Import-PSSession $sfboSession -AllowClobber
}

And that’s it! the code above is a bit rough around the edges but that is because it is just a simple function I use for my own personal preference. To use it, all you have to do is type Connect-Office365 and there you go 🙂

If you encounter any issues or have questions, let me know! and as always, Be careful and happy PowerShelling!

Adding Remote App File associations via PowerShell

We have a lot of RemoteApp and RDS deployments out in the field. Sometimes users want to use the RemoteApp to directly open the correctly associated applications, like .docx for Word files. When using Windows 8 or Windows 10 this can be done automatically by entering the RemoteApp Connection Default URL via the group policy found at the following location:

User Configuration -> Policies -> Administrative Templates ->Windows Components->Remote Desktop Services->RemoteApp and Desktop Connections->Specify default connection URL

This works great if all the clients are in the same domain as the RDS farm or if you are not running RemoteApps from within RDS. When running in a different domain the default File Associations are not applied because the user credentials do not match the credentials on the server-side, also Microsoft does not support deploying the default RemoteApp connections within a RDS Farm. The event log will show you the error:

The installation of the default connection has been cancelled. A default connection cannot be used on a system that is part of a Remote Desktop Services deployment. 

This does mean you can add the Default Connection via a logon script or by manually configuring the URL in the Control panel. The downside of this is that the File Associations are not added to the users register. To resolve this I’ve created a function that creates reg keys for each user to correct this. The usage of the function is pretty straight forward; all you have to do is place the .RDP file and the .ICO file on a public location(e.g. C:\Users\Public\ or a file share) and run the following command:

Add-RemoteAppDefaultProgram -RemoteAppName "Winword Remote App" -Extension ".txt" -RDPFile "C:\Users\Public\Winword Remote App.rdp" -IconFile "C:\Users\Public\Winword Remote App.ico"

The script takes an array as the Extention value, so you can add multiple extentions as such;

Add-RemoteAppDefaultProgram -RemoteAppName "Winword Remote App" -Extension ".txt",".doc",".log" -RDPFile "C:\Users\Public\Winword Remote App.rdp" -IconFile "C:\Users\Public\Winword Remote App.ico"

The script is included below, a slight warning: the script overwrites the current file association for the extention. as always, be careful, and happy powershelling!

<code>&lt;#
.SYNOPSIS

Adds a remote app to the "OpenWith" file assosciations options in Windows 8/10, useful for when using RemoteApps within RDS collections, or when RemoteApp is not fully supported.

.DESCRIPTION

Adds a remote app to the "OpenWith" file assosciations options in Windows 8/10, useful for when using RemoteApps within RDS collections, or when RemoteApp is not fully supported.

Warning: Overwrites current File Assoc settings.

.PARAMETER RemoteAppName
Specifies the RemoteApp name. Strongly advised to keep this the same as the RDCollection name (e.g. "Winword Remote App")

.PARAMETER Extension
Specifies the extension you want to make default. Allowed to specify multiple extentions. ".txt",".log"

.PARAMETER RDPFile
Specifies the .RDP file to use.

.PARAMETER IconFile
Specifies the .ICO file to use. 

.INPUTS

None. Does not accept pipeline input.

.OUTPUTS
lists created registry keys with verbose output.

.EXAMPLE

PS> Add-RemoteAppDefaultProgram -RemoteAppName "Winword Remote App" -Extension ".txt" -RDPFile "C:\Users\Public\Winword Remote App.rdp" -IconFile "C:\Users\Public\Winword Remote App.ico"


.EXAMPLE

PS> Add-RemoteAppDefaultProgram -RemoteAppName "Winword Remote App" -Extension ".txt",".doc",".log" -RDPFile "C:\Users\Public\Winword Remote App.rdp" -IconFile "C:\Users\Public\Winword Remote App.ico"


.LINK

https://www.cyberdrain.com

#>

function Add-RemoteAppDefaultProgram{
 Param(
   [Parameter(Mandatory=$true)]
   [string]$RemoteAppName,
   [Array]$Extension,
   [string]$RDPFile,
   [string]$IconFile

) #end param
$username = $Env:Username
$AppName = $RemoteAppName

foreach($ext in $Extension){
New-Item "HKCU:\Software\Classes$($Ext)" -force -ea SilentlyContinue
New-Item "HKCU:\Software\Classes$($Ext)\OpenWithProgIds" -force -ea SilentlyContinue 
New-ItemProperty -LiteralPath "HKCU:\Software\Classes$($Ext)\OpenWithProgIds" -Name "TSWorkspace.$($AppName)" -Value "" -PropertyType String -Force -ea SilentlyContinue;
}

#Adding Workspace to "OpenWith" registry.
New-Item "HKCU:\Software\Classes\TSWorkspace.$($AppName)" -force -ea SilentlyContinue 
New-Item "HKCU:\Software\Classes\TSWorkspace.$($AppName)\Application" -force -ea SilentlyContinue
New-Item "HKCU:\Software\Classes\TSWorkspace.$($AppName)\CurVer" -force -ea SilentlyContinue 
New-Item "HKCU:\Software\Classes\TSWorkspace.$($AppName)\DefaultIcon" -force -ea SilentlyContinue 
new-Item "HKCU:\Software\Classes\TSWorkspace.$($AppName)\shell" -force -ea SilentlyContinue 
New-Item "HKCU:\Software\Classes\TSWorkspace.$($AppName)\shell\open" -force -ea SilentlyContinue 
New-Item "HKCU:\Software\Classes\TSWorkspace.$($AppName)\shell\open\command" -force -ea SilentlyContinue
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\TSWorkspace.$($AppName)" -Name "(default)" -Value "$($AppName) $($Ext)" -PropertyType String -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\TSWorkspace.$($AppName)" -Name "RemoteApp" -Value 1 -PropertyType DWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\TSWorkspace.$($AppName)\Application" -Name "ApplicationName" -Value "$($AppName)" -PropertyType String -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\TSWorkspace.$($AppName)\Application" -Name "ApplicationDescription" -Value "$($AppName)" -PropertyType String -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\TSWorkspace.$($AppName)\Application" -Name "ApplicationIcon" -Value "`"$($IconFile)`",0" -PropertyType String -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\TSWorkspace.$($AppName)\Application" -Name "ApplicationCompany" -Value "Applicaties" -PropertyType String -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\TSWorkspace.$($AppName)\CurVer" -Name "(default)" -Value "TSWorkspace.$($AppName)" -PropertyType String -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\TSWorkspace.$($AppName)\DefaultIcon" -Name "(default)" -Value "`"$($IconFile)`",0" -PropertyType String -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath "HKCU:\Software\Classes\TSWorkspace.$($AppName)\shell\open\command" -Name "(default)" -Value "`"%systemroot%\system32\mstsc.exe`" /REMOTEFILE:`"%1`" `"$($RDPFile)`"" -PropertyType ExpandString -Force -ea SilentlyContinue;

}</code>

Using PowerShell to check Pwned passwords (Using the HaveIBeenPwned API)

We’ve been encountering a lot of Office365 hacks in the previous months. Most of the time the client does not want MFA enabled and has no clue their password has already been leaked. We figured internally that we’d like a way to check if a single password has been leaked but as we are the purest of nerds we hate browsing to a website. 😉 Enter PowerShell! We’ve created a small script that checks multiple passwords using the HaveIBeenPwned API to check if the password has been seen in a leak before.

To generate a hash of the password we are entering, we’re using Get-StringHash made by Jon Gurgul. To find the get-stringhash function you can visit the PowerShell gallery here or read Jon’s blog about it here

$passwords = @("PlainTextPassword","NotMyPassword","WhyGodWhy","password")

#http://jongurgul.com/blog/get-stringhash-get-filehash/ 
Function Get-StringHash([String] $String,$HashName = "SHA1") 
{ 
$StringBuilder = New-Object System.Text.StringBuilder 
[System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))|%{ 
[Void]$StringBuilder.Append($_.ToString("x2")) 
} 
$StringBuilder.ToString() 
}


foreach($Password in $passwords){
$hash = Get-StringHash -String $($Password) -HashName SHA1
$APICall = $hash.Substring(0,5)
$APIResult = Invoke-Restmethod -Uri "https://api.pwnedpasswords.com/range/$APICall"
$APIresult = $APIresult -split "`n"
$FoundMatch = $APIResult | Where-Object {$_ -match $Hash.Substring(5)}
if($FoundMatch){ write-host "Hash for password $($Password) has been found. Password has most likely been pwned" }
}

The script itself is pretty simple, enter the passwords you want to check into the $Passwords array and run the script, First we’ll load Jon’s function and after that we loop through the password array and write out if the password is encountered.

Attention: Passwords are printed as plain-text and must be entered as plaintext, as always. be careful and happy PowerShelling!

Running Office365 Powershell scripts cross tenant

When you manage multiple tenants or have a Microsoft Partner account to manage a lot of tenants it often gets annoying having to redeploy the same scripts to each tenant over and over. You can try using the MSOL commands but most of the times the scripts you are trying to run are Exchange, Skype for business, or Teams scripts, and you’re not able to run these just by using the Azure Active Directory Module for PowerShell.

The great thing is that Microsoft actually has a perfect solution for this by using your partner credentials cross tenant. You can change the session URL to match the specific client you are trying to manage, this means you can deploy scripts across all clients with relative ease when using the following scripts.

To get started on this, I’ll demonstrate how to get all mailbox sizes across your multiple tenants.
First, we’ll have to connect to Office365 by using the MSOL Microsoft Azure Active Directory Module for Windows PowerShell 

connect-msolservice
$clients = Get-MsolPartnerContract -All

When we’re connected, our client list is filled in $clients. This means we can loop trough each client and perform actions for them. In our case we’ll simply be gathering information, but you could also change settings such as disabling clutter, or enabling other functionality.

foreach ($client in $clients) { 
$ClientDomain = Get-MsolDomain -TenantId $client.TenantId | Where-Object {$_.IsInitial -eq $true}
    Write-host "Logging into portal for $($client.Name)"
    $DelegatedOrgURL = "https://ps.outlook.com/powershell-liveid?DelegatedOrg=" + $ClientDomain.Name
    $ExchangeOnlineSession = New-PSSession -ConnectionUri $DelegatedOrgURL -Credential $credential -Authentication Basic -ConfigurationName Microsoft.Exchange -AllowRedirection
    Import-PSSession -Session $ExchangeOnlineSession -AllowClobber -DisableNameChecking

Now that we’re connected to the Exchange PowerShell session for this client we can run whaterver Exchange commands we’d like. in our case, getting the mailboxes and respective size:

Get-Mailbox | Get-MailboxStatistics | Select-Object DisplayName, IsArchiveMailbox, ItemCount, TotalItemSize | format-table

When we’ve performed our commands, we do need to destroy our session as PowerShell sessions on Office365 can be rate limited.

Remove-PSSession $ExchangeOnlineSession
}

See that final curly bracket? With that one we close our for-each loop, Simply put we’re telling PowerShell that the loop is over, and it can continue with the next client in the list. If we put all of this together our script will look like this:

$credential = Get-Credential
connect-msolservice -Credential $credential
$clients = Get-MsolPartnerContract -All
foreach ($client in $clients) { 
    $ClientDomain = Get-MsolDomain -TenantId $client.TenantId | Where-Object {$_.IsInitial -eq $true}
    Write-host "Logging into portal for $($client.Name)"
    $DelegatedOrgURL = "https://ps.outlook.com/powershell-liveid?DelegatedOrg=" + $ClientDomain.Name
    $ExchangeOnlineSession = New-PSSession -ConnectionUri $DelegatedOrgURL -Credential $credential -Authentication Basic -ConfigurationName Microsoft.Exchange -AllowRedirection
    Import-PSSession -Session $ExchangeOnlineSession -AllowClobber -DisableNameChecking
    Get-Mailbox | Get-MailboxStatistics | Select-Object DisplayName, IsArchiveMailbox, ItemCount, TotalItemSize | format-table
    Remove-PSSession $ExchangeOnlineSession
}

So that’s it. you can easily replace the command for something as disabling clutter, focused inbox, enable auditing, etc. The great thing is that by combining this with an Azure Function gives your the ability to automatically apply your preffered settings to all clients. Happy PowerShelling!

Automatically mapping SharePoint sites in the OneDrive for Business client

Microsoft recently announced that the OneDrive for business client will support automatically mapping SharePoint sites – Which is something I’ve been working on to script for some time now. Automapping sites will be a great benefit for migrations where users are saying goodbye to the good old file system, and hello to SharePoint online. Combined with Files on Demands most users won’t even know the difference.

The only sad part about it is that my pretty cool logon script will become useless fairly soon, anyway I’m sharing this to everyone. Its still in a pretty unfinished state, but it might help you if you are currently struggling with this:

<#
.SYNOPSIS
Automaps the Onedrive client to sync with a sharepoint library


.DESCRIPTION
Run the script with all mandatory parameters

In order to use the parameters you need to fetch them from the applicable site.
Every _api link should have tenantname.sharepoint.com/ in front of it if it's the default site, or tenantname.sharepoint.com/sites/SITENAME for any other site.
If the _api data is hard to read, use a tool like https://codebeautify.org/xmlviewer


.PARAMETER siteid
_api/web
Search for "D:Id", here you will find the SiteID Guid


.PARAMETER webid


.PARAMETER listid
_api/web/Lists/getbytitle('NAMEOFTHELIBRARY')
Search for "D:Id", here you will find the ListID Guid


.PARAMETER URL
_api/web
Search for "D:web", here you will find the SiteID Guid


.PARAMETER webtitle
Set this to the name of the organisation

.PARAMETER listtitle
Set this to the name of the library

.EXAMPLE

.\SharePoint_Automapping.ps1 -siteid GUID -webid = "GUID" -listid = "GUID" -URL https://company.sharepoint.com/ -webtitle Company -listtitle Documents

which will create the following link for the OneDrive app:

start "odopen://sync/?siteId=GUID&webId=GUID&listId=GUID&userEmail=$upn&webUrl=https://company.sharepoint.com/&webtitle=Company&listtitle=Documents" 



.NOTES
In order to retrieve the _api info you need to log in to the tenant, no admin rights are necessary.


#>
param(
[string]$siteid,
[string]$webid,
[string]$listid,
[string]$URL,
[string]$webtitle,
[string]$listtitle
)

$rand = Get-Random -Maximum 10
sleep $rand
#Sleep is required, if the same script runs at the same time it'll mess up the configuration.
$Version = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name ReleaseID | Select-Object ReleaseID
if($version.releaseID -lt 1709) { break }

$strFilter = “(&(objectCategory=User)(SAMAccountName=$Env:USERNAME))”
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = “Subtree”
$objSearcher.PropertiesToLoad.Add(“userprincipalname”) | Out-Null
$colResults = $objSearcher.FindAll()

$UPN = $colResults[0].Properties.userprincipalname
$path = "C:\Users$($env:username)\Lime Networks BV\Lime Networks - $($listtitle)"
if(Test-Path $path){
#DoNothing
} else {
start "odopen://sync/?siteId=$siteid&webId=$webid&listId=$listid&userEmail=$upn&webUrl=$URL&webtitle=$webtitle&listtitle=$listtitle" 
}

Deploying Auto-VPN or Always-On VPN with SSTP

Hi All,

Sorry for the break in blogs about monitoring – I’ve been quite busy with work, so I haven’t had the time to create a monitoring blog. I have been able to create a blog about deploying Always-on VPN, or as Microsoft used to call it “Auto-VPN”. Always-on VPN is going to be the replacement for DirectAccess. DirectAccess was a technology that created 2 hidden VPN tunnels over SSL and encrypted all the data between your client machine and your local network. The downside was that it required Windows Enterprise.

Warning: Long read 🙂
Continue reading

Monitoring with PowerShell Chapter 2: DHCP Pool status

Hi All,

As I’ve explained in my previous the series is taking a bit of a turn here and we’re going to start some blogs about remediation instead of just monitoring. I’ll link back to a previous blog and will explain how we automatically react to these issues within our RMM, if you do not have an RMM – Don’t worry! We’ll include the monitoring + remediation script so you can combine the scripts any way you’d like.

The second monitoring and remediation we’re getting on is a full DHCP-scope and auto-remediate when the scope is completely full. We’ll monitoring several aspects such as the amount of free IP’s, the scope status and lease-time, we’ll also try to clean the scope if it reaches a full state for very old leases or BAD_ADDR’s. Remember that if you bump into this issue a lot it’s better to increase scope size or manage your devices and network 🙂

Continue reading

Monitoring with PowerShell Chapter 2: Hyper-V replication and remediation

Hi All,

As I’ve explained in my previous the series is taking a bit of a turn here and we’re going to start some blogs about remediation instead of just monitoring. I’ll link back to a previous blog and will explain how we automatically react to these issues within our RMM, if you do not have an RMM – Don’t worry! We’ll include the monitoring + remediation script so you can combine the scripts any way you’d like.

The first monitoring and remediation we’re getting on is Hyper-V replication, where we will try to resolve the replication if the server was unreachable for some time, force a resync, and clear the statistics.

Continue reading

Monitoring with PowerShell Chapter 2: Monitoring Windows Activation State

Hi All,
Today we will be focusing on monitoring the status of the Windows and office activation status, we use an imaging application to supply all of our machines with the correct version of Windows, but sometimes its forgotten to actually activate the OS. This script checks the current activation status and alerts on it.

Continue reading