Kundenzufriedenheit Jungfraubahnen

Nun einmal etwas anderes als immer nur IT :). Ich muss einfach meinen Ärger über die Jungfraubahnen los werden:

Wir (3-köpfige Familie) mussten im August 2024 eine Busse von 2x 90 Fr. bezahlen, weil wir aus Versehen ohne gültigen Fahrausweis in den Jungfraubahnen (von Grindelwald nach Interlaken wohlgemerkt) unterwegs waren. Ich hatte beim Kauf des Tickets mittels SBB App nicht darauf geachtet, dass die App vom Ticket auf den 1.-Klass-Zuschlag wechselt, wenn man diesen anwählt. Das ursprünglich ausgewählte Ticket wird dann nicht dazu gebucht.

Dass man auf dieser Strecke ohne gültiges Ticket unterwegs ist, ist zweifach unlogisch:

  • es findet IMMER eine Kontrolle statt
  • wieso sollte ich NUR den 1.-Klass-Zuschlag, nicht aber ein gültiges Ticket kaufen?

Natürlich wurden wir kontrolliert, und mussten eben die Busse bezahlen. Die Zugbegleiterin hatte absolut keine Einsicht ab unserem Versehen.

Ich wollte im Zug aber keinen grossen Aufstand machen, denn ich ging davon aus, dass wir das offensichtliche versehen nachträglich lösen können.

Weit gefehlt.

Ich wandte mich an per Mail an direktion@jungfrau.ch. Leider wurde überhaupt nicht auf unser Problem eingegangen, obwohl mir bestätigt wurde, dass es sich offensichtlich um ein Versehen handelte.

Ich wandte mich dann auch an Stefan Würgler, seines Zeichens „Leiter Betrieb Eisenbahn“. Ich hoffte, dass er etwas Berufsstolz zeigen würde, und seinen Betrieb ins richtige Licht rücken möchte. Leider auch hier: absolut keine Einsicht, keine Kulanz, keine Kundenfreundlichkeit.

Im Anschluss dachte ich mir: „OK, mindestens das Tourismusbüro wird genug anständig sein, um meinem Problem beachtung zu schenken“. Weit gefehlt. Ich meldete mich bei „GRINDELWALD TOURISMUS“ (ja, die schreiben sich gross). Doch sogar hier: ich erhielt nur eine überhebliche, kundenUNfreundliche Antwort.

Eigentlich hatten wir als Familie Grindelwald eben erst (seit letztem Jahr) lieb gewonnen. Wir waren 2x auf der Pfingstegg, und wollten dies weiterführen. Immer mit Mittagessen, beide Male mit Nachtessen. Wir überlegten uns sogar, einmal da zu übernachten.

Die Moral von dieser Geschichte: Grindelwald scheint es nicht nötig zu haben, eine junge schweizer Familie für ihre Destination zu begeistern. Das ist tragisch.

Wir jedenfalls werden NIE mehr in diesem Gebiet verkehren. Und dies auch all unseren Bekannten so weiter erzählen.

Auf tripadvisor.ch habe ich eine entsprechende Bewertung hinterlegt.

Auch wenn all das diesen Institutionen in Grindelwald komplett egal sein wird :(.

eigene Blog-Domain

Ab sofort werde ich nicht mehr hier (https://wordpress.com/post/itblogwildi.wordpress.com), sondern auf meiner eigenen Domain bloggen: https://itblog.wildi.dk/

Alle Einträge wurde auf die neue Domain übertragen. Leider verliere ich die Statistiken. Aber ich hoffe, weiterhin gelesen zu werden 🙂

Jellyfin auf Alpine Linux startet nicht

Ich habe Jellyfin auf einer Alpine Linux VM am laufen. Nach den letzten Updates mittels

apk update
apk upgrade

konnte Jellyfin nicht mehr gestartet werden:

jellyfin [ failed ]

Ich stellte fest, dass unter /etc/conf.d zwei conf-Dateien für Jellyfin existieren:

-rw-r–r– 1 root root 163 Jul 12 14:25 jellyfin
-rw-r–r– 1 root root 163 Jul 12 14:17 jellyfin.apk-new

Ich vermute, dass während dem Update die Datei mit der Endung „.apk-new“ erstellt wurde. In dieser ist auch eine Option enthalten, welche in meiner originalen nicht vorhanden ist:

webdir=“/usr/share/webapps/jellyfin-web“

Ich habe diese Zeile in der ursprünglichen Datei „jellyfin“ hinzugefügt, und konnte den Dienst danach erfolgreich starten.

rc-service jellyfin start

New Teams auf Remote Desktop Session Host installieren

Um das neue Teams auf einem Remote Desktop Session Host (RDSH) installieren zu können, muss man in die Trickkiste greifen. Denn Microsoft unterstützt kein Verteilen als Admin für alle Benutzer, wie dies auf Clients der Fall ist.

Wir brauchen das MSIX-Setupfile. Hier findet ihr den Link, hier die Direktlinks für 64bit und 32bit.

Wir nutzen für diesen Vorgang das „sideloading“. Dies muss zuerst in der Registry erlaubt werden:

reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowAllTrustedApps" /d "1"

Danach muss das MSIX wie folgt auf dem RDSH installiert werden:

dism /Online /Add-ProvisionedAppxPackage /PackagePath:\vm-srv18-fa\GPOAPPS$\Microsoft\Teams\MSTeams-x64.msix /SkipLicense

Das neue Teams steht dann per sofort allen RDSH-Usern zur Verfügung.

ACHTUNG: Sicherheits-sensitive Admins fragen sich, ob die Freigabe zum sideloading nun wieder entfernt werden kann. Dies wäre mit folgendem Befehl möglich:

reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /v "AllowAllTrustedApps" /f

ABER danach funktioniert New Teams nicht mehr!

How to find config/.storage/core.config_entries in HomeAssistant

Um eine Integration automatisch neu starten zu können, benötige ich deren ID. Diese findet sich in der Datei „config/.storage/core.config_entries“.

Doch um diese zu finden, muss das Verzeichnis zuerst freigeschalten werden:

Settings > Add-ons > File editor > Configuration > „.storage“ entfernen > Save

Danach wird der File editor neu gestartet, und das Verzeichnis ist darin sichtbar.

Exchange Online: löschen von gelöschten gelöschten Elementen

Ein User hatte viele (>30GB!) Daten in seiner Mailbox gelöscht, und konnte nun keine weiteren Elemente mehr löschen. Das Verhalten war typisch: im Outlook Client wird die Mail einfach nicht gelöscht. D.h. ich markiere ein Element, drücke Shift-Delete, bestätige die Warnung – … und nix passiert. Kein Fehler, kein Löschen.

In OWA konnten die Mails gelöscht werden, erschienen dann aber eine Sekunde später wieder im selben Ordner.

Wichtig, für folgende Befehle muss mit ExchangeOnline sowie Security & Compliance hergestellt werden:

Connect-ExchangeOnline
Connect-IPPSSession

Dieser Befehl zeigt das Problem:

Get-MailboxFolderStatistics $User -FolderScope RecoverableItems | FL Name,FolderAndSubfolderSize,ItemsInFolderAndSubfolders
Name : Recoverable Items
FolderAndSubfolderSize : 30 GB (32,214,533,730 bytes)
ItemsInFolderAndSubfolders : 80220
Name : Audits
FolderAndSubfolderSize : 268.9 MB (282,008,901 bytes)
ItemsInFolderAndSubfolders : 47373
Name : Calendar Logging
FolderAndSubfolderSize : 0 B (0 bytes)
ItemsInFolderAndSubfolders : 0
Name : Deletions
FolderAndSubfolderSize : 95.98 MB (100,641,633 bytes)
ItemsInFolderAndSubfolders : 73
Name : Purges
FolderAndSubfolderSize : 29.65 GB (31,831,883,196 bytes)
ItemsInFolderAndSubfolders : 32774
Name : SubstrateHolds
FolderAndSubfolderSize : 0 B (0 bytes)
ItemsInFolderAndSubfolders : 0
Name : Versions
FolderAndSubfolderSize : 0 B (0 bytes)
ItemsInFolderAndSubfolders : 0

Im Ordner „Recoverable Items“ („gelöschte Elemente“) befanden sich die noch (definitiv) zu löschenden Elemente.

Im Ordner „Purges“ befinden sich die Elemente, welche zwar bereits definitiv gelöscht wurden, aber durch Exchange Online noch zurückbehalten werden (falls es sich der Benutzer – trotz dreimaligem löschen – noch anders überlegt).

Ich habe hier absichtlich meine gesamte „Reise“ dargestellt. Die Lösung gibt es ganz unten.

Zuerst habe ich die Anleitung von Microsoft befolgt:

Get-Mailbox $User | FL SingleItemRecoveryEnabled,RetainDeletedItemsFor
Get-CASMailbox $User | FL EwsEnabled,ActiveSyncEnabled,MAPIEnabled,OWAEnabled,ImapEnabled,PopEnabled
Get-Mailbox $User | FL LitigationHoldEnabled,InPlaceHolds
Get-OrganizationConfig | FL InPlaceHolds
Get-Mailbox $User | FL DelayHoldApplied,DelayReleaseHoldApplied
Get-MailboxFolderStatistics $User -FolderScope RecoverableItems | FL Name,FolderAndSubfolderSize,ItemsInFolderAndSubfolders
Get-Mailbox $User | FL RetainDeletedItemsFor,SingleItemRecoveryEnabled,ElcProcessingDisabled
Set-Mailbox $User -RetainDeletedItemsFor 30
Set-Mailbox $User -SingleItemRecoveryEnabled $false
Set-Mailbox $User -ElcProcessingDisabled $true
Get-Mailbox $User | FL DelayHoldApplied,DelayReleaseHoldApplied

Danach muss man übers GUI eine neue Suche erstellen, für den entsprechenden Ordner (bei mir war das /Purges folderid:0A4ABD8C1C5F934283D2D2ED83724B370000000001170000)

Nun kann ich die gefundenen Elemente löschen:

New-ComplianceSearchAction -SearchName "i4MWsearch2" -Purge -PurgeType HardDelete

Das geht ganz fix. Nur wenige Sekunden. Der Nachteil: pro Befehl-Ausführung (sagt man dem so?) werden ganze 10 (zehn!!) Elemente gelöscht.

Ich habe knapp 38’000!!!

38’000!

Und unter „Recoverable Items“ sogar 80’000 Elemente!

Es kann doch nicht sein, dass ich hier ein extra Script schreiben muss, um eine Microsoft-Anleitung in einem Loop auszuführen (leider war das dann doch so, siehe unten).

Der Vollständigkeit halber, mit diesen Befehlen wird dies abgeschlossen:

Get-ComplianceSearchAction
Remove-ComplianceSearchAction "i4MWsearch2_purge"

Schlussendlich konnte ich den Ordner mit folgenden Befehlen leeren:

Zuerst die Mailbox vorbereiten:

Set-Mailbox $User -LitigationHoldEnabled $False
Set-Mailbox $User -RemoveDelayHoldApplied
Set-Mailbox $User -RetainDeletedItemsFor 0
Set-Mailbox $User -SingleItemRecoveryEnabled $false

60 Minuten warten

Start-ManagedFolderAssistant $User -HoldCleanup

Mit

Get-MailboxFolderStatistics $User -FolderScope RecoverableItems | FL Name,FolderAndSubfolderSize,ItemsInFolderAndSubfolders

kann dann wieder überprüft werden, ob’s geklappt hat.

Doch die Story geht weiter. Leider hat das nix gebracht. Ich habe dann die Retention-Policy geprüft:

Get-Mailbox $user | select *Retention*

Hier war die Default-Policy aktiv. Mittels folgendem Befehl kann die entfernt werden:

Set-Mailbox $user -RetentionPolicy $null

Leider passierte auch damit nichts. Ich habe mich entschlossen, wieder den Weg von Microsoft einzuschlagen, aber halt ein Script zu schreiben/finden, welches den Löschvorgang in einem Loop ausführt.

Hier gibt es ein ganzes Toolset, welches u.a. diese Funktion beinhaltet. Aber das war mir dann too much.

In diesem Spiceworks Thread habe ich dann meine Lösung gefunden: Das Script des Users jonesbones. Ich habe es leicht angepasst, um den Ordner noch als Variable zu setzen. Ausserdem habe ich den foreach-Teil angepasst, dass das Löschen schneller abläuft:

$ScriptStart = Get-Date
$SearchName = 'DeleteSearch1'
$Mailbox = 'user@domain.ch'
$Folder = '/Purges'
# oder: $Folder = '/Recoverable Items'

$FolderStatistics = Get-MailboxFolderStatistics -Identity $Mailbox

# Convert Inbox folder ID to ID that can be used in compliance search
# https://docs.microsoft.com/en-us/microsoft-365/compliance/use-content-search-for-targeted-collections
$InboxFolderId = ($FolderStatistics | Where-Object {$_.FolderPath -eq $Folder}).FolderId
$Encoding= [System.Text.Encoding]::GetEncoding("us-ascii")
$Nibbler= $Encoding.GetBytes("0123456789ABCDEF");
$FolderIdBytes = [Convert]::FromBase64String($InboxFolderId);
$IndexIdBytes = New-Object byte[] 48;
$IndexIdIdx=0
$FolderIdBytes | select -skip 23 -First 24 | %{$indexIdBytes[$indexIdIdx++]=$nibbler[$_ -shr 4];$indexIdBytes[$indexIdIdx++]=$nibbler[$_ -band 0xF]}
$FolderQuery = "folderid:$($encoding.GetString($indexIdBytes))"

$Items = ($FolderStatistics | Where-Object {$_.FolderId -eq $InboxFolderId}).ItemsInFolder

$Iterations = [math]::Ceiling($Items / 10)

Remove-ComplianceSearch $SearchName -Confirm:$false # wird benötigt, wenn Script zuvor abgebrochen wurde

    New-ComplianceSearch -Name $SearchName -ExchangeLocation $Mailbox -ContentMatchQuery $FolderQuery | Out-Null
    Start-ComplianceSearch -Identity $SearchName | Out-Null
    Do {
        Start-Sleep -Seconds 2 # Adding 2 sec wait in case of possible timeouts from MS end
        $ComplianceSearch = (Get-ComplianceSearch -Identity $SearchName)
        #Write-Host "-- CS $($ComplianceSearch.Status), $($ComplianceSearch.Items)"
    } While ($ComplianceSearch.Status -ne 'Completed')

foreach ($Iteration in @(1..$Iterations)) {
    Write-Progress -Activity "Purging $Folder" -Status "$($ComplianceSearch.Items) items left" -PercentComplete ($Iteration / $Iterations * 100)
    Write-Host "[$Iteration / $Iterations]" #$($ComplianceSearch.Items) items left"
    New-ComplianceSearchAction -SearchName $SearchName -Purge -PurgeType HardDelete -Confirm:$false | Out-Null
    Do {
        #Start-Sleep -Seconds 1
        $PurgeAction = Get-ComplianceSearchAction -Identity "$SearchName`_Purge"
        #Write-Host "-- PA $($PurgeAction.Status)"
    } While ($PurgeAction.Status -ne 'Completed')
    
}

Remove-ComplianceSearch $SearchName -Confirm:$false

$ScriptEnd = Get-Date
$ExecutionTime = $ScriptEnd - $ScriptStart
Write-Host "$ExecutionTime"

Das Script läuft dann einfach ne (lange, seeehr lange) Weile, weil extrem ineffizient – danke Microsoft :|.

M365 / Microsoft Cloud: Lizenz einer einzelnen App deaktivieren

Wieso einfach, wenn’s kompliziert geht? Diese Frage stelle ich mir in letzter Zeit immer öfter, wenn’s um Microsoft, und insbesondere deren Cloud M365 geht.

Hier ein schönes Beispiel: ich will nur einen Haken einer App entfernen. Im GUI brauche ich dazu nur ein paar Klicks:

Um dasselben mittels Powershell zu lösen, benötige ich, sagen wir, ein paar Schritte mehr:

$User = "user@domain.ch"
## Get the services that have already been disabled for the user.
$userLicense = Get-MgUserLicenseDetail -UserId $User
$userDisabledPlans = $userLicense.ServicePlans | Where ProvisioningStatus -eq "Disabled" | Select -ExpandProperty ServicePlanId

## Get the new service plans that are going to be disabled
$e5Sku = Get-MgSubscribedSku -All | Where SkuPartNumber -eq 'TEAMS_ESSENTIALS_AAD'
$newDisabledPlans = $e5Sku.ServicePlans | Where ServicePlanName -in ("EXCHANGE_S_DESKLESS") | Select -ExpandProperty ServicePlanId

## Merge the new plans that are to be disabled with the user's current state of disabled plans
$disabledPlans = ($userDisabledPlans + $newDisabledPlans) | Select -Unique

$addLicenses = @(
    @{
        SkuId = $e5Sku.SkuId
        DisabledPlans = $disabledPlans
    }
)
## Update user's license
Set-MgUserLicense -UserId $User -AddLicenses $addLicenses -RemoveLicenses @()

Naja, nicht sehr effizient, Microsoft :(.

Quelle: https://learn.microsoft.com/en-us/microsoft-365/enterprise/disable-access-to-services-with-microsoft-365-powershell?view=o365-worldwide

Löschen von public Folder Mailboxen schlägt fehl

Ich wollte einen Exchange-Server migrieren, und währenddessen die noch vorhandenen onPrem-Mailboxen der PublicFolder löschen. Die PublicFolder waren schon lange migriert, aber die Postfächer hat irgendjemand nicht gelöscht ;).

Der Vorgang wäre hier schön beschrieben.

Doch Exchange wollte nicht:

Get-Mailbox -PublicFolder | where {$_.IsRootPublicFolderMailbox -eq $False} | Remove-Mailbox -PublicFolder

Es konnten keine aktiven Postfächer für öffentliche Ordner gefunden werden. Entweder wurden keine Postfächer für
öffentliche Ordner bereitgestellt, oder sie wurden im HoldForMigration-Modus bereitgestellt. Wenn Sie aktuell keine
Migration ausführen, erstellen Sie ein Postfach für öffentliche Ordner.
+ CategoryInfo : NotSpecified: (:) [Remove-Mailbox], ObjectNotFoundException
+ FullyQualifiedErrorId : [Server=SRV,RequestId=a6c72a8d-c6cb-4c15-a5cc-068977ab21b4,TimeStamp=18.04.2024 19:
28:51] [FailureCategory=Cmdlet-ObjectNotFoundException] 159E9A7C,Microsoft.Exchange.Management.RecipientTasks.Remo
veMailbox
+ PSComputerName : srv.domain.local

Die Einstellungen schienen aber alle OK:

Get-OrganizationConfig | fl RemotePublicFolderMailboxes,PublicFoldersEnabled
RemotePublicFolderMailboxes : {}
PublicFoldersEnabled        : Remote

Ich fand heraus, dass ich die Mailboxen mit folgendem Befehl löschen kann:

Remove-Mailbox -PublicFolder -Identity "PFMBX01" -Force

Ich habe dann den Befehl aus der Anleitung hier angepasst:

Get-Mailbox -PublicFolder -ResultSize Unlimited | ?{$_.IsRootPublicFolderMailbox -ne "True"} | Remove-Mailbox -PublicFolder -Force

und:

Get-Mailbox -PublicFolder | ? {$_.IsRootPublicFolderMailbox -eq "True"} | Remove-Mailbox -PublicFolder -Force

So konnte ich die Public Folder Mailboxen erfolgreich löschen.

AzureMfaNpsExtnConfigSetup.ps1 kommt nicht mit deutschen Systemen klar

Wenn ich das Script ausführe, erhalte ich folgende Fehlermeldung:

Ausnahme beim Aufrufen von "SetAccessRule" mit 1 Argument(en): "Manche oder alle Identitätsverweise konnten nicht
übersetzt werden."
In C:\Program Files\Microsoft\AzureMfa\Config\AzureMfaNpsExtnConfigSetup.ps1:92 Zeichen:2 
$acl.SetAccessRule($buildAcl) #Add Access Rule
 ~~~~~~~~~
 CategoryInfo : NotSpecified: (:) [], MethodInvocationException
 FullyQualifiedErrorId : IdentityNotMappedException 

Nach der Analyse des Scripts, hat Microsoft hier mal wieder bei den Sprachen gepatzt. Die original Zeile:

Dies musste ich auf folgendes anpassen:

Der Benutzer „NETWORK SERVICE“ hat die SID „S-1-5-20“. Damit konnte ich den korrekten Namen auf dem deutschen System idenfizieren.

Erstelle eine Website wie diese mit WordPress.com
Jetzt starten