Jere's Techblog

Reset User Profile FatClients

Just run the Script and have some fun while deleting local/remote Userprofiles 🙂

The parameters Username and ComputerName are mandatory.

The parameter -wildcard:$true allows to delete multiple profiles. For example all users with the profile name “John*“.

#by J.Kühnis 
#Code Elements of https://gallery.technet.microsoft.com/scriptcenter/Remove-UserProfileps1-871f57c4
#Run with elevated rights
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent( ) )
if ( -not ($currentPrincipal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator ) ) )
{
    Write-Host "This script must be executed in admin mode." -ForegroundColor Yellow
    Write-Error "This script must be executed in admin mode." -ErrorAction Stop
    Pause
}

Function Reset-LocalUserProfile {

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)][string]$Username,
        [Parameter(Mandatory = $true)][string]$ComputerName,
        [switch]$IncludeSpecialUsers = $False,
        [switch]$Force = $True,
        [bool]$Wildcard   
)

    IF ($Username -match '\*'){
        IF($Wildcard){
            Write-Warning "wildcard enabled, deletion for multiple users enabled"

        }Else{
            Write-Warning "Username must be unique without wildcard '*'. If you like to use wildcard, please use '-Widlcard `$true' parameter. "
        return
        

        }
    }
        
    
    $profileFounds = 0

    #Region Functions

    #https://www.petri.com/test-network-connectivity-powershell-test-connection-cmdlet
    Function Test-PSRemoting {
        [cmdletbinding()]
     
        Param(
            [Parameter(Position = 0, Mandatory, HelpMessage = "Enter a computername", ValueFromPipeline)]
            [ValidateNotNullorEmpty()]
            [string]$Computername,
            [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty
        )
     
        Begin {
            Write-Host -Message "Starting $($MyInvocation.Mycommand)"  
        } #begin
     
        Process {
            Write-Host -Message "Testing $computername"
            Try {
                $r = Test-WSMan -ComputerName $Computername -Credential $Credential -Authentication Default -ErrorAction Stop
                $True 
            }
            Catch {
                Write-Host $_.Exception.Message
                $False
     
            }
     
        } #Process
     
        End {
            Write-Host -Message "Ending $($MyInvocation.Mycommand)"
        } #end
     
    } #close function

    #Check IF WinRM is OK

    IF (!(Test-PSRemoting -Computername $ComputerName)) {    
        Write-Host -Message "PS Remoting Error, can't reach Connect with WinRM"
        return
        
    }
    

    Try {
        $profiles = Get-WmiObject -Class Win32_UserProfile -Computer $ComputerName -Filter "Special = '$IncludeSpecialUsers'" -EnableAllPrivileges
    }
    Catch {            
        Write-Warning "Failed to retreive user profiles on $ComputerName"
        return
    }

   
    ForEach ($profile in $profiles) {
        try {
            $sid = New-Object System.Security.Principal.SecurityIdentifier($profile.SID)               
            $account = $sid.Translate([System.Security.Principal.NTAccount])    
            $accountName = $account.value.split("\")[1]
            $profilePath = $profile.LocalPath
            $loaded = $profile.Loaded
            $special = $profile.Special
        }
        catch {
            continue
    
        }
            
        If ($accountName.ToLower() -Eq $UserName.ToLower() -Or ($UserName.Contains("*") -And $accountName.ToLower() -Like $UserName.ToLower())) {
      
            #If ($ExcludeUserName -ne [string]::Empty -And -Not $ExcludeUserName.Contains("*") -And ($accountName.ToLower() -eq $ExcludeUserName.ToLower())) {Continue}
            #If ($ExcludeUserName -ne [string]::Empty -And $ExcludeUserName.Contains("*") -And ($accountName.ToLower() -Like $ExcludeUserName.ToLower())) {Continue}

            $profileFounds ++

            If ($profileFounds -gt 1) {Write-Host "`n"}
            Write-Host "Start deleting profile ""$account"" on computer ""$ComputerName"" ..." -ForegroundColor Green
            Write-Host "Account SID: $sid"
            Write-Host "Special system service user: $special"
            Write-Host "Profile Path: $profilePath"
            Write-Host "Loaded : $loaded"
            If ($loaded) {
                Write-Warning "Cannot delete profile because is in use"
                Continue
            }

            If ($Force -Or $PSCmdlet.ShouldProcess($account)) {
                Try {
                    $profile.Delete()           
                    Write-Host "Profile deleted successfully" -ForegroundColor Green        
                }
                Catch {            
                    Write-Host "Error during delete the profile. Maybe the user with you executed the script has no rights or the script was not started with admin rights." -ForegroundColor Red
                }
            } 
        }
    }

    If ($profileFounds -eq 0) {
        Write-Warning "No profiles found on $ComputerName with Name $UserName"
    }
Write-Host '########## START SCRIPT ##########' -ForegroundColor yellow
Reset-LocalUserProfile
}

Reset-LocalUserProfile
Continue reading...

Get Data from Bluecat DNS Server with REST API

Here is an example how you can use the REST API on the BluecatDNSServer to query data via the workflow interface (alternatively you could use its API directly).
I am sure that you can use this concept for other web interfaces.

The script is a translation of a CURL request. It shows how to query the token and use this “BASIC Token” for further queries.

#BY J.Kühnis
#Invoke Webrequest/RestMethod to get IP Adress & Mac-Adress from Bluecat API
#translation of CURL Commands

############################
#   CURL sample
#
#Get TOKEN
#curl -k https://URL/rest_login -X POST -H "Content-Type: application/json" --data "{\"username\":\"your USERNAME\",\"password\":\"your USERNAME\"}"
#
#GET Request:
#curl -k https://URL/get_ip_infos/get_ip_infos_endpoint -X GET -H "auth: Basic ****SOME TOKEN****" -H "Content-Type: application/json" --data "{\"host\":\"SERVERNAME\"}"
#
#
#result:
#{
#  "ip": "some ip",
#  "mac": "some mac"
#}
############################




#Trust SelfSigned SSL/TLS Channel
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@ 
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy


#Generate Web Token (Basic Token)
Function Get-WebTokenBasic{

    [CmdletBinding()]
    Param(
    [Parameter(Mandatory = $true)][string]$Username,
    [Parameter(Mandatory = $true)][string]$Password
    )

$json=ConvertTo-Json (@{"username"="$Username";"password"="$Password";})
$token = (Invoke-WebRequest -Uri "https://URL/rest_login"  -Body $json -ContentType "application/json" -Method POST).content | Out-String | ConvertFrom-Json

$token = $token.access_token
$global:headers = @{auth="Basic $token"}

}

#Get IP or Mac from Servername
Function Get-DNSBluecatValues{
[CmdletBinding()]
Param(
    [Parameter(Mandatory = $true)][string]$ServerName
    
)

$json4= (@{"host"="$servername";}) | ConvertTo-Json

Try
{
    Get-WebTokenBasic
    $result = Invoke-WebRequest -Uri "https://URL/get_ip_infos/get_ip_infos_endpoint" -Headers $headers -Body $json4 -Method Post -ContentType "application/json"
    $global:result = $result | ConvertFrom-Json
    return $global:result
}
Catch
{
    $ErrorMessage = $_.Exception.Message
    $FailedItem = $_.Exception.ItemName
    Write-Warning "Failed Authentication or Webrequest"
    Break
}


}
Continue reading...

MCLI Module error after Citrix PVS Update 7.13 to 7.18

After Updating Citrix PVS Server i got an Issue with my PVS Scripts.
I couldn’t load the Powershell modules anymore.

Of course I have properly registered the DLL of the PVS Snapin. I executed the following command as admin in the CMD:

"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe" "c:\program files\citrix\provisioning services console\Citrix.PVS.snapin.dll"

I got this Error:

PS C:\Temp> Add-PSSnapin *

Add-PSSnapin : Cannot load Windows PowerShell snap-in McliPSSnapIn because of the following error: The Windows PowerShell snap-in module C:\Program Files\Citrix\Provisioning Services

Console\McliPSSnapIn.dll does not have the required Windows PowerShell snap-in strong name McliPSSnapIn, Version=7.13.0.13008, Culture=neutral, PublicKeyToken=null.

Solution:

The support article https://support.citrix.com/article/CTX226178 describes some symptomps but the Solution didn’t work.

In my case I had to manually change certain registry keys.

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\PowerShellSnapIns\McliPSSnapIn]

You Can also Copy this into a .REG File and run on the PVS Server (Ensure the Versionnumber is equal to your PVS Version):

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\PowerShellSnapIns\McliPSSnapIn]
"PowerShellVersion"="5.1"
"Vendor"="Citrix Systems, Inc."
"Description"="This is a PowerShell snap-in that includes the Mcli-Add, Mcli-Delete, Mcli-Get, Mcli-Help, Mcli-Info, Mcli-Run, Mcli-RunWithReturn, Mcli-Set and Mcli-SetList cmdlets."
"ApplicationBase"="C:\\Program Files\\Citrix\\Provisioning Services Console"
"ModuleName"="C:\\Program Files\\Citrix\\Provisioning Services Console\\McliPSSnapIn.dll"
"CustomPSSnapInType"="McliPSSnapIn.McliPSSnapIn"
"Version"="7.18.0.106"
"AssemblyName"="McliPSSnapIn, Version=7.18.0.106, Culture=neutral, PublicKeyToken=null"

Continue reading...

Delete Citrix Worker from Studio and vCenter

With this script one or more servers can be deleted from the Citrix DeliveryController (Citrix Studio) and from the ESXi/vCenter.

To use The Script some variables and values need to be adjusted like the name of the Citrix DeliveryController and vCenter.
Vmware (PowerCLI) and Citrix (SDK) powershellmodules need to be installed.

This only works if the VM name is identical to the Worker Server DNS name. If this is the case, the following string can be deleted in the script [-replace “.FQDN.address”,””]

In my case, the name of the VM is only the “hostname” of the machine and not the DNSname. So the script removes the FQDN name, in order to use the script successfully, this must also be adjusted.

Import-Module *
Add-PSSnapin *

$DeliveryController = "someBrokerDNSName"
Connect-viserver "some vCenter"


Get-BrokerMachine -DNSName anySevernames* -AdminAddress $DeliveryController |  %{
    
    #Delete & Remove From Citrix Studio
    Remove-BrokerMachine $_ -DesktopGroup $_.DesktopGroupName
    Remove-BrokerMachine $_ -Force

    #Delete Permanently from vCenter
    remove-vm ($_.DNSName -replace ".FQDN.Adress","") -DeletePermanently -Confirm:$false

    write-host $_.DNSName -ForegroundColor Green  #Write ServerName

}
Continue reading...

Useful WordPress Plugins

I’m a big WordPress fan!
The community is huge, it has lots of plugins and it is very user friendly.
Compared to a CMS like Typo3, simple end users can manage WordPress very easily and create posts, as well as customize pages.

This post shows some of my favorite addins and explains their function:

WPTouch – Responsive Mobile Theme;easy to manage

Simple Login Screen Customizer –the name says it all

Secret Content – hide content from non logged in visitors

Post Expirator – The Post Expirator plugin allows the user to set expiration dates for both posts and pages.

NextGEN Gallery – The most popular gallery plugin for WordPress and one of the most popular plugins of all time with over 24 million downloads.

Members – Members is a plugin that extends your control over your blog. It’s a user, role, and capability editor plugin that was created to make WordPress a more powerful CMS.

Contact Form 7  & Advanced Contact form 7 DB – A solid contact form plugin and extension that exports the database in formats such as CSV, Excel, etc.

Continue reading...

Migration mounted shared Mailboxes from Outlook 2010 to Outlook 2016

During the migration from Server 2008 R2 – Citrix 6.5 to Server 2016 Citrix 7.15, the customer had a special request: that the language settings, printer mappings and Outlook shared mailboxes be transferred to the new Server2016/Office 2016 environment. Of course, we don’t want to copy the full Windowsprofile, instead we want only use the most necessary settings to keep the new profile as clean as possible.
With the printer mappings and language settings there are so far no problems, all this can be found “relatively simply” in the Registry and taken over (Powershell is your friend).

With the Outlook shared mailboxes it was more complicated…we had in this specific case no possibility to get the relation between the assigned shared mailboxes and the users trough Exchange…the Exchange admin told us that you can’t read the relations with the current Exchangeserver settings.

Although you can technically read the members of a shared mailbox with get-mailbox or a similar command.

Probably you could also mount the shared mailboxes automatically to Outlook…if you like…but honestly, that’s not the point. And
i’m not an MS-Exchange professional…😉

You can actually get Outlook profiles/mounted shared mailbox (profiles can be seen under  CMD: “c:\Windows\SysWOW64\control.exe mlcfg32.cpl“)from the user registry. It may not be the best way but it works and offers a small advantage. You migrate only those shared mailboxes which the user has assigned to himself in the current Outlook version. You will not randomly migrate all shared mailboxes to the new Outlook environment just because the user is a member of those.

To cut a long story short…
What needs to be done now to migrate the mounted mailboxes/profiles from Office 2010/Server2008R2 to Office2016/Server2016?
First an export of the Outlook profiles:
HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles

Watch your step! This cannot be imported easily…because the path has changed ( Thank you Microsoft!).

New path under Office 2016 (Import to these location):
HKCU\SOFTWARE\Microsoft\Office\16.0\Outlook\Profiles

There are a lot of cryptic registry keys but so far the migration and the Outlookprofiles hasn’t caused any problems. Write me in the comments, if you know the specific key’s which are needed to migrate shared mailboxes (so I can make everything a less streamlined and cleaner)…I don’t take this trouble today 🙂

Automation:

#Registry Functions writte by https://administrator.de/user/colinardo/ @ https://administrator.de/forum/powershell-registry-sichern-wiederherstellen-ohne-reg-exe-regedit-exe-367223.html
#Region variables

[string]$Outlookprofile = "$root\Outlookprofile.xml"

#Region RegKey Function
function Export-RegTree([string]$regkey,[string]$exportpath){
    $data = @()
    $createobject = {
        param($k,$n)
        [pscustomobject] @{
            Name = @{$true='(Default)';$false=$n}[$n -eq '']
            Value = $k.GetValue($n)
            Path = $k.PSPath
            Type = $k.GetValueKind($n)
        }
    }
    get-item $regkey -PipelineVariable key| %{
        $key.GetValueNames() | %{$data += . $createobject $key $_}
    }
    gci $regkey -Recurse -Force -PipelineVariable key | %{
        $key.GetValueNames() | %{$data += . $createobject $key $_}
    }
   $data | Export-Clixml $exportpath
}

function Import-RegTree([string][ValidateScript({Test-Path $_})]$xmlfile){
    Import-Clixml $xmlfile | %{
        if (!(Test-Path $_.Path)){md $_.Path -Force | out-null}
        New-ItemProperty -Path $_.Path -Name $_.Name -Value $_.Value -PropertyType $_.Type -Force
    }
}

#region Outlook Profile

function Export-Outlookprofile{
    Export-RegTree -regkey 'HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles' -exportpath $Outlookprofile
    LOG -TEXT "EXPORT : Outlookprofile $Outlookprofile"
}

function Import-Outlookprofile{
 Invoke-Command -ScriptBlock {start-process outlook} 
     sleep 10
    $Replace = Get-Content $Outlookprofile
    $Replace | % {$_.Replace("HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles","HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles")} | Set-Content $Outlookprofile
    Import-RegTree -xmlfile $Outlookprofile
    LOG -TEXT "IMPORT : Outlookprofile"
}

You have to define the $root variable, it can be something like ‘$root = “$env:appdata\Profilmigration7x”‘ or a Network share. If you use the Appdata path, ensure to Copy the data to the “NEW” appdata-path if you use Citrix or another UPM versioning tool.

Continue reading...

Powershell Core installation on Linux

There are several ways to install Powershell on Linux/Max/Windows. The usual way is to download the installer package:

https://github.com/PowerShell/PowerShell/releases/

I prefer to add the package repository to the System to keep the installation up2date when you update your linux system/apps.

Microsoft has a very nice Documentation about the Powershell install:

https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-6

Continue reading...