Jere's Techblog

Blog CONTENTS

We are drowning in information, but starved for knowledge. - John Naisbitt

Cloud

This area is all about private cloud, SaaS,IaaS,PaaS but also about solutions like Nextcloud,OwnCloud,Seacloud,OneDrive

Linux

If you want to know more about the OpenSource Penguin, you've come to the right place. Learn more about what you can do with the OS from the 90s

Network

Network professional? I'm sure I'm not, but I'm sure you'll find some hints about useful tools. Or maybe you just want to use a SSH service as proxy, tunnel.

Scripting & Programming

A large part of this blog area is about Powershell, but in the future there will be more content like SQL,C#,VBS,.Net etc. as well.

Web

HTML5, CMS, CSS and respsonsive are all unfamiliar terms? Then this is the right place for you, I am not a webprofessional, but I can give some start tips to the newbies.

Windows

With over 80% market share, Windows is the world's leading operating system. Tips & tricks, tutorials, troubleshooting and much more can be found in this section.

Citrix

It's all about Terminal Services, NetScaler, VDI, XenApp, XenDesktop, StoreFront, CitrixDirector and Citrix Delivery Controller.

Hypervisor

Hypervisors, where our virtual machines are hosted. Installation Guides, Comparisons, Configurations and more..

news

Troubleshooting an App-V issue on a PVS image

This blog entry is based on the following, a little bit outdated article:

The problem is unfortunately still current it just brings some changes with Microsoft Server 2016. Therefore I took the liberty to copy some explanations from the “old article”.

Problem Description

During a recent application implementation project, we ran into the following issue. Some App-V applications, which were installed locally in the PVS image, were unable to start or they were throwing various error messages. One of the applications which were showing errors, was MS Office (Word, Excel, Powerpoint) viewer. The error thrown at startup of the application was the following: “The operating system is not presently configured to run this application.”

The App-V client was 5.0 SP2, but App-V 5.0 SP3 was also used as a troubleshooting step. The PVS target devices were configured as such:
Streamed disk
Persistent disk (flush overflow)
Re-installing the application in the image, solved the issue for that particular application. However, we wanted to know the root cause of this issue, so we logged a case at Citrix. This was done because the behavior was not existing on a regular non-streamed VM.
After extensive troubleshooting together with Citrix support we discovered the root cause:
One of the registry keys (HKLM\SOFTWARE\Microsoft\AppV\MAV\Configuration\Packages\<package ID>) containing the PackageRoot value contained a faulty HarddiskVolume number.
This volume was indicated as 3, whereas the streamed disk volume number was 2.

Solution

Correcting HarddiskVolume3 into HarddiskVolume2 did solve the issue.
The reason why the HarddiskVolume3 value got into the registry was because these applications where installed in the image on the template machine. This was the machine from which the initial vdisk was captured. During the installation of the particular App-V applications, the original disk was still attached .


This is issue is definitely not Citrix or PVS related, but can be encountered on such a setup.
To prevent this issue from happening again in the future, the original disk was detached from the template VM.

You can get the disknumber in CMD with “DISKPART -> ListDisk” or PowerShell “Get-Disk”.

My expirience and solution with this issue on Server 2016:

In our Environment we ran into the same problem and we could fix this on Server 2008R2 & Citrix 6.5, PVS 7.15 with the modification of one of the registry keys (with a Startupscript during worker boot). (HKLM\SOFTWARE\Microsoft\AppV\MAV\Configuration\Packages\<package ID>) containing the PackageRoot value contained a faulty HarddiskVolume number.

On Server 2016 (Citrix 7.17) everything was useless. Creating or deleting a disk on the masterimage or changing the RegKey didn’t work. I also didn’t find a way to change the disk volume number.

The problem could only be solved by adjusting the call parameter of the application. If the application is started in the App-V context it works fine.

As an example:

Microsoft InfoPath 2013

C:\ProgramData\Microsoft\AppV\Client\Integration\D24C3BDD-8FAD-44D3-998C-933F8F053682\Root\Office15\INFOPATH.EXE /appvve:d24c3bdd-8fad-44d3-998c-933f8f053682_6b0281c5-bb0b-49fb-b52c-a6651e8ed2ed

filetype associations fix

to fix the filetype associations you need to add the “/appvee:**APPV-ID***” parameter to the registry root class:

HKEY_CLASSES_ROOT\InfoPath.Solution.4\shell\Open\command

“C:\ProgramData\Microsoft\AppV\Client\Integration\D24C3BDD-8FAD-44D3-998C-933F8F053682\Root\Office15\INFOPATH.EXE” /appvve:d24c3bdd-8fad-44d3-998c-933f8f053682_6b0281c5-bb0b-49fb-b52c-a6651e8ed2ed “%1”

Query big ADObject / Containers

The Powershell AD-Modules have certain restrictions when it comes to querying large objects, this can be bypassed by ADSI Query.

Here an example how to read them and how to iterate on users of a group:

#by J.Kühnis 13.03.2019
#example ADSI Query Powershell
$BigGroup = "someADGroupName"
$ADGroup1 = "someADGroup1"
$ADGroup2 = "someADGroup2"


$groupname = $BigGroup
$groupdsn = (Get-ADGroup $groupname).DistinguishedName
$group =[adsi]”LDAP://$groupdsn” 
$groupmemebrs = $group.psbase.invoke("Members") | % {$_.GetType().InvokeMember("SamAccountName",'GetProperty',$null,$_,$null)}

$groupmemebrs | foreach {
    
    $usradgroups = GET-ADUser -Identity $_ –Properties MemberOf | Select-Object -ExpandProperty MemberOf | Get-ADGroup -Properties name | Select-Object name
    IF ($usradgroups.name -notContains $ADGroup1 -and $usradgroups.name -notContains $ADGroup2) {

        #User is not a Member of ADGroup1 & ADGroup2

        }
        Else{
          #User is MemberOf ADGroup1 & ADGroup2
        }
}

Here also an example using a Class to just specify the object the way you like:

#by J.Kühnis 09.10.2019
#Set variables
$ADGROUP = "someAdGrp"

# Load AD-Module
IF (!(Get-Module -Name ActiveDirectory)) {
    Import-Module -Name ActiveDirectory
    IF (!(Get-Module -Name ActiveDirectory)) {
    start-sleep 10
    Write-Warning "No AD-Module Found"

        Exit
    }
}
$groupname = $ADGROUP
$groupdsn = (Get-ADGroup $groupname).DistinguishedName
$group =[adsi]”LDAP://$groupdsn” 
$groupmemebrs = $group.psbase.invoke("Members") | % {$_.GetType().InvokeMember("SamAccountName",'GetProperty',$null,$_,$null)}

class User{
[string]$Name
[string]$SamAccountName
[string]$UserPrincipalName
[string]$Mail
[string]$extensionAttribute7
[string]$extensionAttribute9
}


$Userlist = @()
$groupmemebrs | ForEach-Object {
    $usrATTR = GET-ADUser -Identity $_ –Properties Name,SamAccountName,UserPrincipalName,mail,extensionAttribute9, extensionAttribute7
    
    $User = [User]::new()
    $User.Name = $usrATTR.Name
    $User.SamAccountName = $usrATTR.SamAccountName
    $User.UserPrincipalName = $usrATTR.UserPrincipalName
    $User.Mail = $usrATTR.Mail
    $User.extensionAttribute1 = $usrATTR.extensionAttribute1
    $User.extensionAttribute2 = $usrATTR.extensionAttribute2

    $Userlist += $User
}
$Userlist | format-table

By

Read More

Copy-Items with folder Structure and Filter in a Foreach-Parallel Workflow

#J.Kühnis 08.03.2019
$Sourcefolder= "\\localhost\C$\Temp\1"
$Targetfolder= "C:\Temp2\1"


$query = Get-ChildItem $Sourcefolder -Recurse | Where-Object {$_.LastWriteTime -gt [datetime]::Now.AddDays(-1)}


workflow CopyJob {
    param (
    [Object[]]$query,
    [String]$Sourcefolder,
    [String]$Targetfolder
    )
    
Foreach($item in $query){
    $dest = $Targetfolder + $item.FullName.SubString($Sourcefolder.Length)
    
    #Write-Host $dest -ForegroundColor Yellow
    Copy-Item $item.FullName -Destination $dest -Force
}
}

CopyJob -query $query -Sourcefolder $Sourcefolder -Targetfolder $Targetfolder

By

Read More

Invoke Command on Specified MachineCatalog and DeliveryGroup

The nice thing about Powershell and the modules/API to other technologies is that you can do simple queries and have a big effect.

The following example starts a service for specified machines in a Citrix 7.x environment.

#by J.Kühnis 06.03.2019
Add-PSSnapin *
$machines = (Get-BrokerMachine * -AdminAddress my.broker.fqdn |
 where-object {($_.CatalogName -match "someMC*") -and ($_.DesktopGroupName -eq "someDG")}).DNSName

Foreach ($machine in $Machines)
 {Write-Host $machine -ForegroundColor Yellow
 invoke-command -ComputerName $machine -ScriptBlock {get-service -name cpsvc | Start-Service} }

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

By

Read More

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
}


}

By

Read More

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"

By

Read More

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

}