Category Archives: Microsoft

Artikels die een relatie hebben met Microsoft producten

Find where a certificate is installed

Some organizations use wildcard certificates. The problem with these certificates is that they start to lead a life on it’s own. So when the time comes that such a certificate is about to expire one can wonder where is this certificate installed? Luckily we have Powershell to save us from having to open all certificate stores on all servers in the domain. At first I was  thinking maybe the easiest way could be to have a Powershell remoting session. But that would require all servers to have Powershell remoting enabled. Unfortunately not all servers have it enabled or have a reasonable Powershell version to start with. So just for documenting purpose I’ll write down the command to find a certificate.

A certificate has a thumbprint. This thumbprint is equal on all servers as it part of the certificate, much like the serial number.  So you can easily find the thumbprint where your looking for running the command on a server where the certificate is present.

Just copy the thumbprint and enter it into the command to find the certificate like the on below

Invoke-Command {gci cert:\Localmachine\My | ? {$_.Thumbprint -eq "FF9C130AE6C1D18FAC43AF0416E44B7011307545"}} -ComputerName #COMPUTERNAME#

Replace #COMPUTERNAME#  for the computername you would want to question. Now this is all good but like I mentioned before I would want to find certificate regardless of Operation system contstrains or whether Powershell Remoting is enabled or not. So this script would process that accordingly. Change the thumbprint to the thumbprint of your certificate. The script export all servers where the certificate with the given thumbprint is present. Please feel free to modify this to your needs.

Function Get-Cert( $computer=$env:computername ){
    #Sets ReadOnly flag upon external Certificate store.
	$ro=[System.Security.Cryptography.X509Certificates.OpenFlags]"ReadOnly"
	#Opens LocalMachine store. You could potentially enter a different Cert store. 
	$lm=[System.Security.Cryptography.X509Certificates.StoreLocation]"LocalMachine"
	#Opens personal certificate store from local machine. 
	$store=new-object System.Security.Cryptography.X509Certificates.X509Store("\\$computer\My",$lm)
	#Establish connection with remote PC
    $store.Open($ro)

	#Display certificates in console or variable
	$store.Certificates

}

#Opens list
$Thumbprint = "FF9C130AE6C1D18FAC43AF0416E44B7011307545" 
$List = @()

#Servers reset their machine password within a specific timeframe
$Servers = Get-ADComputer -Filter * -Properties OperatingSystem,PasswordLastSet | ?{$_.OperatingSystem -match "Server" -and $_.PasswordLastSet -gt (Get-Date).AddDays(-60)}

Foreach ($Server in $Servers)
	{
	$Server.DNSHostName 
	If ((Get-Cert $Server.DNSHostname | ? {$_.Thumbprint -eq $Thumbprint}).Count -ne 0)
		{
		$List += $Server.DNSHostName
		}
	}

$List | % {AC -Value $_ -Path ".\ComputersWithCertificate.txt"}

Count homedirectories

For management purposes creating a report with the directory size of users’s home directory you can use this script. It will count the total size of the home directory per user. It will get the user’s name based on the name of the home directory. This wil be written down in the file.

$list = @(GCI "\\Fileserver\users$" -Force| ?{$_.PsIsContainer -eq $True})
$Header1 = ("DisplayName;Gebruikersnaam;Grootte in MB")
$Header2 = ("Hoofdmap;Grootte in MB")
$Path1 = ".\Overzicht-H-Schijven.csv"
AC -Value $Header1 -Path $Path1
$Path1 = GI $Path1


Foreach ($Item in $List)
{
$Result = @(GCI ($Item.FullName) -Recurse -Force)
[Int]$Count = 0
Foreach ($ResultItem in $Result){$Count = $Count + $ResultItem.Length / 1MB}
$Value = ((Get-ADUser $Item.Name).Name + ";" + $Item.Name+ ";" + $Count)
AC -Value $Value -Path $Path1
}

$Body = "Beste,

Hierbij het overzicht van de grootte van de H Schijven van de gebruikers van uw organisatie.

Groet,
"
$list = Import-CSV $Path1.FullName -Delimiter ";"
$list | % { $_."Grootte in MB" = [int]$_."Grootte in MB" }
$list = $list | Sort -Property Gebruikersnaam -Unique | Sort -Property "Grootte in MB" -Descending
$list | Export-CSV $Path1.FullName -Delimiter ";" -NoTypeInformation -Force

Send-MailMessage -From "servicedesk@company.nl" -Subject "Gebruiker overzicht " -To "emailadress@domain.tld" -Body $Body -SmtpServer "mailserver" -Attachments $Path1.FullName

RI $Path1 -Force

Connecting to Powershell Endpoints Exchange/Lync

I will write a short post about connecting to Powershell endpoints of Exchange and Lync. Basically you can always use PSRemoting to connect and make use of modules. However some CMDlets will simply not work when importing the snapin or module. So connecting to an endpoint is much more efficient. To connect to Microsoft Exchange use the following commands:

$PSOptions = New-PSSessionOption -SkipCACheck -SkipRevocationCheck -SkipCNCheck
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ExchangeServer.Domain.TLD/powershell -SessionOption $PSOptions
Import-PSSession $Session 

To connect to Lync use the following commands:

$Session = New-PSSession  -ConnectionUri https://LyncServer.Domain.TLD/ocspowershell -Authentication NegotiateWithImplicitCredential
Import-PSSession $Session

You can connect from any domain computer. Of course if your loged on with an account that does not have to appropriate permission you’ll have to connect using the  -credentials parameter.

 

ShareFile edit Storage quota limit

I just wrote an opinion about the Powershell module of Citrix ShareFile. Now let’s get to it. This is the fun part. You’ll have to take a close look at how Citrix describes things. So if you want to manage the StorageQuotaLimitGB field using Powershell, you can use the script below. I’ll get in detail about it.

Add-PSSnapin ShareFile
$SFClient = Get-SfClient -Name File.sfps
$User = Send-SfRequest $SFClient -Method GET -Entity "Accounts\Employees" | ?{$_.Email -match "EmailAddress@Domain.TLD"}
$Property = new-object ShareFile.Api.Models.AccountUser
$NewQuota = ([int]$Quota = (Send-SfRequest $SFClient -Method GET -Entity "users" -Id $User.id).StorageQuotaLimitGB) + "5"
$Property.StorageQuotaLimitGB = $NewQuota
$Uri = $user.url.OriginalString.replace("Contacts","Users/AccountUser")
$Result = Send-SfRequest -Client $SFClient -Method Patch -Uri $Uri  -Body $property
If ($Result.StorageQuotaLimitGB -notmatch $NewQuota){Write-Error "Value is niet aangepast"}

This script will export all users and then select the user with the e-mailaddress specified. You can of course enter a string there with a specific value. Then it creates a new property with the value AccountUser. See it like this you want to edit this value but it is not in the Entity Users but one endpoint lower. It is in the entity AccountUser. AccountUser is below the entity User. If you for example would enter -Entity “Users”, the script will fail because the value StorageQuotaLimitGB is simply not present at that level. At first I got this wrong.  So that’s why you set this value lower. It is described on the site of Citrix but just a bit cryptic. Look for this part of the text:

StorageQuotaLimitGB

So then the script get the current limit and adds an additional 5 GB to that limit. This is caught in a integer. Than I get the URL. Please not that here in the EU we have a different link. This script is not impacted by that but this explanation is. ShareFile uses this link:

https://ShareFileDomainName.sf-api.eu/sf/v3/Users(ID)

Well if you use the Entity Users the link above will be used. As said above you’ll want to point to the endpoint AccountUser. So the script changes the current uri from contacts to this:

https://ShareFileDomainName.sf-api.eu/sf/v3/Users/AccountUser(ID)

That is the magic. Next the script sends a request to the REST API of ShareFile to apply the new StorageQuotaLimitGB. At last it checks if the change is actually applied. If it isn’t a error is written.

Powershell ShareFile

When a software developer introduces a Powershell module or snapin I’m always excited about it. So Citrix released a Powershell snapin for ShareFile. There are some good thing about it, some bad things and one ugly thing.

The bad

So Citrix says that the ShareFile module is without the guarantee that it’ll actually work. See it like a beta product. This directly indicates that you’ll find very few documentation about. But even more worrying is that you’re on your own because it is unsupported. Citrix released a Powershell version but at the same time there’s a C# and a Java module. They’ve build this on a REST API. This is beautiful because in a way you’re not really bound to the ShareFile modules. As long as you can talk to the REST API. The API is on version 3 so I think it’s pretty stable and should lose it “in development” state.  But the lack of good documentation and

The Ugly

Well first and foremost, an administrator can create a SFPS file. This file can be used to authenticate to the ShareFile REST API. So you can create a carefree file that could be used by a first in line administrator or a script. And so you don’t have to share the credentials.  By default there’s no expiration date. Meaning that if this file falls in the wrong hands, one can truly demolish your entire environment. A lazy administrator could potentially compromise his entire environment. Via Powershell you can actually get the files in the repository of a ShareFile User. This is a extreme risk of which many administrators are unaware.

The Good

Well there’s a lot good about this. In most cases if there’s a REST API, the GUI is build upon that. So anything you can do in the GUI, can be done using the API. This is an incredible feature set. You can completely manage your environment using the API. I created some scripts to manage the ShareFile API. Right now the community is rather small, but as soon as this grows I’m sure there’s going to be a large knowledge base where you can find help.

If you get the authentication right and the scripts ready I’m sure that this will ease the pressure of the back of the administrators. I’m quite positive anyway.

Correct data in mobile field in AD

A script changed the format of values in the Mobile field in AD. This however caused errors in the operation of soft tokens. I made an export of all mobile phone numbers and noticed that there were more errors. As so I’ve made a script that uses the substring method to correct the values. Please feel free to change it as desired.

$List2 = @()
$List = Get-ADUser -Filter * -Properties Mobile | ?{$_.Mobile -ne $Null}
Foreach ($Item in $List)
	{
	If ($Item.Mobile.Substring(0,2) -notmatch "06" -or $Item.Mobile.Length -ne "10")
		{
		$List2 += $Item
		}
	}

Foreach ($Item in $List2)
	{
	If ($Item.Mobile.Substring(0,3) -match "316"){Set-ADUser -Identity $Item.sAMAccountName -MobilePhone ($item.mobile.Substring(0,3).Replace("316","06") + $Item.Mobile.Substring(3))}
	If ($Item.Mobile.Substring(0,1) -match "6"){Set-ADUser -Identity $Item.sAMAccountName -MobilePhone ($item.mobile.Substring(0,1).Replace("6","06") + $Item.Mobile.Substring(1))}
	If ($Item.Mobile.Substring(0,4) -match "\+316"){Set-ADUser -Identity $Item.sAMAccountName -MobilePhone ($item.mobile.Substring(0,4).Replace('+316',"06") + $Item.Mobile.Substring(4))}
	If ($Item.Mobile.Substring(0,5) -match "00316"){Set-ADUser -Identity $Item.sAMAccountName -MobilePhone ($item.mobile.Substring(0,5).Replace("00316","06") + $Item.Mobile.Substring(5))}	
	If ($Item.Mobile.Substring(0,3) -match "013"){Set-ADUser -Identity $Item.sAMAccountName -MobilePhone $Null}	
	If ($Item.Mobile -match " "){Set-ADUser -Identity $Item.sAMAccountName -MobilePhone ($Item.Mobile.Replace(" ",""))}
	If ($Item.Mobile -match "-"){Set-ADUser -Identity $Item.sAMAccountName -MobilePhone ($Item.Mobile.Replace("-",""))}
	If ($Item.Mobile -match "Geen"){Set-ADUser -Identity $Item.sAMAccountName -MobilePhone $Null}
	}

Query number of group memberships per user

There’re times when things don’t go your way. Recently I’ve had a migration where both the old and the new company drives were active. Since we use AGLP, each folder has at least double the amounts of groups. Considering that the old company drive is also active, double that amount and you’ll have yourself a issue where users are unable to log on. The max token size is limited to a maximum of 1015 group memberships. This includes the nested groups. Since I wanted to know how big this problem was I decided to create a script that outputs this data to a file. All you have to do is enter the distinguished name of the OU you want to do a search in or remove the searchbase parameter completely to run it for all users.

Ipmo ActiveDirectory
$List = Get-ADUser -SearchBase "OU=OUName,DC=DOMAIN,DC=LOCAL" -Filter * -Properties MemberOf
$List2 = @()
$Path = ".\GroupMembershipsPerUser.csv"
ForEach ($Item in $list)
	{
	$Result = @()
	ForEach ($Group in $Item.MemberOf)
		{
		$Result += $Group
		$GroupResult =@(Get-ADGroup $Group -Properties Memberof).Memberof
		$Result += $GroupResult
		$Temp = $GroupResult
		While ($Temp -ne $Null)
			{
			$Temp = @()
			Foreach ($Object in $Groupresult)
				{
				$Objectresult =@(Get-ADGroup $Object -Properties Memberof).Memberof
				If ($Objectresult -ne $Null)
					{
					$Temp += $Objectresult
					$Result += $ObjectResult
					}
				}
			If ($Temp.count -ne 0){$Groupresult = $Temp}
			}
		}
	$Result = $Result | Select -Unique
	$List2 += $Item.SAMAccountName	+ ";" + $Result.count
	Write-host $item.Name heeft $Result.count groepen -f Yellow -b DarkCyan
	}	
AC -Path $Path -Value "Username;Groups"
ForEach($Item in $List2)
		{
		AC -Path $Path -Value $Item
		}

Connecting to servers outside a domain using Powershell

From time to time you’ll find yourself running scripts to a server outside the domain. If you have the same credentials on that as on the domain there’s no problem. However in most cases you don’t. Normally you will have to encrypt the password as a secure string. However there are cases that such a solution costs to much time. Especially if you have multiple passwords for the same user account (eg. Administrator). So to get this done you might use a old trick to connect to the server using SMB en store the credentials session in the memory of the server running the script. I must admit that this is only if you’ll have to go quick and dirty. If’ve used it once and it may come in handy sometimes.

$Credentiallist = @()
$Credentiallist += "Password1"
$Credentiallist += "Password2"
$Credentiallist += "Password3"
$Credentiallist += "Password4"
$Errors = ".\Error.csv"
$Success = ".\Success.csv"
$net = new-object -ComObject WScript.Network
$ADComputers = Import-CSV ".\DNSNames.csv"
$Computers =@()
$ComputerSuccess =@()
$FailedComputers = @()
Foreach ($ADComputer in $AdComputers)
	{
	$ADComputer = $ADComputer.DNSHostName
	Foreach ($cred in $credentialList)
		{
		If ($ComputerSuccess -notcontains $ADComputer)
			{ 
			$net.MapNetworkDrive("u:", "\\$ADComputer\c$", $false, "localhost\Administrator", "$Cred")
			If((Test-Path "\\$ADComputer\C$\Windows" ) -eq $True)
				{
				$ComputerSuccess += $ADComputer
				#Do the code you want to run against the server
				}
			}
		}
	If ((get-psdrive -name "U" -Erroraction SilentlyContinue) -ne $null){$net.RemoveNetworkDrive("u:")}
	}

Find user session on all active servers

If you want to find a session on all active servers in the domain you can make use of the command quser /server:whatever. This displays the session details from that server. This is handy if some administrator has locked his account because of old sessions on some server. There’re alternative ways to find this. I made a post awhile back. See it here.

IPMO ActiveDirectory

$Username = "SAMACCOUNTNAME"
$List2 = @()

$List = Get-ADComputer -Properties passwordlastset,operatingsystem -Filter * | ?{$_.PasswordLastSet -gt (Get-Date).Adddays(-30) -and $_.OperatingSystem -match "Server"}

Foreach ($item in $list){
	[String]$Server = $item.Name
	$Result =@( (quser /server:$Server) -replace '\s{2,}',','|ConvertFrom-Csv)
		Foreach ($object in $result){
			If ($Object.UserName -Match $Username){$Output = ($Server + ";" + $Object.UserName + ";" + $Object.State)
			$List2 += $Output
		}
	}
} 

Compare AD User Properties

An administrator had to resolve an issue with a user account that could not sync with Share file. He thought that he had tried everything and so wanted to compare an Active Directory user that he copied from the trouble account with the actual user. The copied user did not have any trouble. After comparing he found that the user was member of an Active Directory Protected Group (aka Admincount=1). I made a Powershell script for him so he could compare the two.

Write-Host "Dit script vergelijkt twee Active Directory Users met elkaar."
$Outputdir = ".\exports\"
$Continue = "Y"
While ($Continue -eq "Y"){

While ($User1 -eq $Null){
$User1Value = Read-Host "Geef eerste user op"
$User1 = Get-ADUser $User1Value -Properties *}

While ($User2 -eq $Null){
$User2Value = Read-Host "Geef tweede user op"
$User2 = Get-ADUser $User2Value -Properties *
}

	$Usercomparison = @()
	$Usercomparison2 = @()

	$User1.GetEnumerator() | % {
		If ($User2.($_.Key) -eq $_.Value.value)
		{
			$Comparison = 'Gelijk'
		}
		else
		{
			$Comparison = 'Verschilt'
		}

		$UserObject = New-Object PSObject -Property ([ordered]@{
			Property = $_.Key
			$User1Value = $_.Value
			$User2Value = $User2.($_.Key)
			Overeenkomst = $Comparison
		})
		$UserObject2 = New-Object PSObject -Property ([ordered]@{
			Property = $_.Key
			$User1Value = $_.Value.Value
			$User2Value = $User2.($_.Key)
			Overeenkomst = $Comparison
		})
		$UserComparison += $UserObject
		$UserComparison2 += $UserObject2
	}

$UserComparison


while ($Difference -notmatch "[y|n]"){$Difference = read-host "Wil je filteren op gegevens die verschillen?(Y/N)"}

If ($Difference -eq "Y"){$UserComparison | ? {$_.Overeenkomst -match "Verschilt"} | FT}

while ($Choice -notmatch "[y|n]"){$choice = read-host "Wil je een export maken van de ongefilterde gegevens? (Y/N)"}
If ($Choice -eq "Y"){
Write-Host "Er wordt een export gemaakt van de bovenstaande gegevens op de locatie:"
If ((Test-Path ("$OutputDir"+"UserComparison-"+"$User1Value"+"$User2Value"+".csv")) -eq $True){
$List = GCI "$OutputDir" | ? {$_.FullName -match ("UserComparison-"+"$User1Value"+"$User2Value")}
[int]$Count = $list.count
$Count++}
If ($Count -ne $null){[string]$Count = ("-"+"$Count")}
$UserComparison2 | Export-csv -Path ("$OutputDir"+"UserComparison-"+"$User1Value"+"$User2Value"+"$Count"+".csv") -NoTypeInformation -Delimiter ";"
$Item = GI ("$OutputDir"+"UserComparison-"+"$User1Value"+"$User2Value"+"$Count"+".csv")
$Item.FullName
}
Remove-Variable User1,User1value,User2,User2value,Choice,Continue,Difference
While ($Continue -notmatch "[y|n]"){$Continue = Read-Host "Wil je nog een vergelijking uitvoeren? (Y/N)"}
}