Skip to main content

14 posts tagged with "Powershell"

View All Tags

Simplify iMessage File Extraction with iMessageExporter

· 4 min read
Hannes Palmquist
Senior Consultant Cloud

iMessage is a fantastic platform for communication, but when it comes to extracting your precious photos, videos, and other files, the process can quickly become daunting. Instead of paying for expensive third-party services, you can take control of your data with the iMessageExporter script. This handy tool simplifies the extraction process, making it free and easy for anyone to use. Here’s how you can make the most of this tool.

What is iMessageExporter?

iMessageExporter is a straightforward PowerShell script that enables users to extract media files, such as photos and videos, from their iMessage backups. The script is designed to save you time and money by providing a free alternative to commercial services that charge for the same task. Whether you're switching phones, archiving memories, or just want to have a backup of your media, iMessageExporter makes it easy.

Step-by-Step Guide: How to Use iMessageExporter

To get started with iMessageExporter, follow these simple steps:

  1. Backup Your iPhone with iTunes Before you can extract any files, you'll need to create a backup of your iPhone using iTunes. Here’s how:

    • Connect your iPhone to your PC or Mac using a USB cable.
    • Open iTunes and select your device when it appears in the top-left corner.
    • Create a backup by clicking on "Back Up Now."
    • Important: Ensure that the backup is not encrypted. This is crucial because iMessageExporter won’t be able to access the data from an encrypted backup.
  2. Locate the iTunes Backup on Your Computer Once the backup is complete, you can locate it on your computer. The default location for the backup depends on your operating system:

    • Windows: C:\Users<username>\Apple\MobileSync\Backup
    • Mac: ~/Library/Application Support/MobileSync/Backup/
  3. Run the iMessageExporter Script With your backup in place, it's time to run the iMessageExporter script. Here’s how:

    • Download and open PowerShell on your computer.
    • Run the script using the following command, replacing the paths with your specific directories:
.\iMessageExporter -iTunesBackupDirectory 'C:\Users\John\Apple\MobileSync\Backup\12345678-1234567890ABCDEF' -ExportDirectory 'C:\Export'

In this command:

  • -iTunesBackupDirectory specifies the path to your iTunes backup.
  • -ExportDirectory defines the folder where you want the extracted files to be saved.

After running the script, you’ll see the files being extracted into your chosen directory. These files could include JPG, HEIC, and other media formats.

Setting Up Dependencies

Before running iMessageExporter, make sure you have the necessary dependencies installed. The script relies on the PowerShell module PSSQLite to interact with the iTunes backup database. You can install it easily using the following command in PowerShell:

Install-Module PSSQLite -Scope CurrentUser

This command installs the module for your current user, ensuring that iMessageExporter can run smoothly.

Acknowledgments

iMessageExporter is built upon the foundation laid by basnijholt, who created a Perl script for exporting iMessages to HTML. While his original script focused on full message exports, iMessageExporter is streamlined to focus on extracting files only. By converting the logic from Perl to PowerShell, iMessageExporter caters to users who are specifically interested in retrieving their media files.

Conclusion

iMessageExporter offers a simple and cost-effective way to extract your iMessage photos, videos, and other files. By following the steps outlined above, you can easily take control of your media without needing to rely on expensive third-party services. Whether you’re tech-savvy or just getting started, iMessageExporter makes the process straightforward, allowing you to preserve your digital memories effortlessly.

Give it a try, and see how easy it can be to manage your iMessage files!

Work with the Immich API from Powershell

· 2 min read
Hannes Palmquist
Senior Consultant Cloud

I've put together a new powershell module which allows you to work with the Immich API. With the API easily accessible you can now write automations for your image gallery. The module has approx 85% API coverage where the remaining 15% is mainly used by the Immich Web application and or mobile app.

You can manage users, assets, albums, libraries, partners, api-keys, activities, configuration, start jobs, sharing links, tags etc.

You can build quite powerful automations for your image gallery, some examples;

  • Automatically create and update albums based on folder structure, exif metadata like tags/keywords
    • For instance, I have a filewatcher script that monitors my on-disk image library, if an image is written/modified on disk, the image keywordw is retrieved with exiftool and if the image has a keyword beginning with "Album_*" it extract the album name, makes sure the album exist in Immich and then makes sure the image is a member of that album. With this script I can update keywords in an external application (in my case Adobe Lightroom) and have those changes reflected in Immich. I plan on publishing a blog post with more details about this setup.
  • Download/Upload assets
  • Hide/Trash/Favorite assets
  • Manage album/asset sharing
  • With a filewatcher you can instruct Immich to refresh thumbnails and/or metadata for an external asset when it is updated/changed.
Install-Module PSImmich -Scope CurrentUser

PSImmich Module Docs

Cleanup GitHub Artifacts

· 2 min read
Hannes Palmquist
Senior Consultant Cloud
info

This script has been integrated in the module PSDev and is no longer provided as a stand-alone script.

At the time of writing this post there is no native automated way to cleanup/remove github artifacts produced within GitHub Action Workflows. If you for instance have an automated build process you will quite fast hit the free storage quota in GitHub and you'll have to start paying for additional storage. There are a few available options to manage this. You could manuallly remove each workflow run that could contain artifacts. This could be timeconsuming work. You could also configure the retension time of workflows in each repo. However both of these will remove the logs of each workflow run as well. A better way if you don't need the artifacts after the workflow run is to add a cleanup job to the workflow. I've been using geekyeggo/delete-artifact@v2. This action till remove the artifacts that i specify in the workflow file automatically within a cleanup job at the end of the workflow. This works great for future workflow runs once you configure the cleanup job. But what about if you have hit the storage quota and need to cleanup all existing artifacts? Then you would have to do it manually or call the GitHub Rest API and remove all artifacts. This is what the below script does, it will enumerate all artifacts for a specific repo or all repos for a user account and remove all artifacts.

Remove-GitHubArtifact -GitHubSecret "ABC" -GitHubOrg "user"

Repo Artifacts_Found Artifacts_Removed Artifacts_SizeMB
---- --------------- ----------------- ----------------
PSDaikin 5 5 43
PSDataSet 2 2 2
PSMaintenanceManager 2 2 21
PSPortainer 34 34 321
PSQueue 0 0 0
PSScriptInfo 0 0 0
PSSort 0 0 0

Or for a specific repo:

Remove-GitHubArtifact -GitHubSecret "ABC" -GitHubOrg "user" -Repo "PSMaintenanceManager"

Repo Artifacts_Found Artifacts_Removed Artifacts_SizeMB
---- --------------- ----------------- ----------------
PSMaintenanceManager 2 2 21
danger

The script will remove all artifacts for the specified repo or all repos on the account. This is destructive and can not be reversed.

Announcing PSPortainer Powershell Module

· One min read
Hannes Palmquist
Senior Consultant Cloud

Some time ago I wanted to automate a process where I needed check status of a few docker containers managed with Portainer and noticed that there was no powershell module available on the gallery for portainer. A couple of minutes later I had discovered the Portainer Rest API and though that it could be a fun project to provide powershell users with the ability to manage their Portainer and Docker instances with powershell.

So here it is, an early pre-release/work in progress powershell module for Portainer, PSPortainer

If you want to contribute to the project it is publically available on GitHub here

To get started, visit the docs for the module at getps.dev or start exploring directly by installing the module from PSGallery

Install-Module PSPortainer -Scope CurrentUser

Unable to write certain characters in Windows Terminal

· One min read
Hannes Palmquist
Senior Consultant Cloud

Recently I noticed a strange issue within Windows Terminal where I was unable to write the "$" sign using Ctrl+Alt+4. (Swedish keyboard layout has Ctrl+Alt+4 as the key combination to write a $ sign). However a workaround was to use Altgr+4 instead which worked fine. This issue was isolated to Windows Terminal as there were no issue writing $ in any other application, ie, notepad, vscode etc.

Luckely the issue for me was simple, with some update to Windows Terminal the development team hi-jacked the shortcut Ctrl+Alt+4 for changing to tab number 3. I removed the shortcut keybinding from the settings page and vola, Ctrl+Alt+4 started working as a $-dollar sign again.

Settings -> Actions -> Look for a shortcut for the key combination that is not working.

The same issue would arise for all keyboard layouts using Ctrl+Alt+X for a character.

In the case of Scandinavian keyboard layouts the following characters is probably affected.

  • Ctrl+Alt+2 -> @
  • Ctrl+Alt+3 -> £
  • Ctrl+Alt+4 -> $
  • Ctrl+Alt+7 -> {
  • Ctrl+Alt+8 -> [
  • Ctrl+Alt+9 -> ]

I though I'd share if anyone else stumbles upon this.

Introducing PSQueue

· One min read
Hannes Palmquist
Senior Consultant Cloud

Recently I developed a tool that utilizes the .NET class system.collections.queue. The function runs continously and produces workitems that should be processed in sequence and in the same order they were added. One simple way to acheive this is to use the collection queue. This class provides a simple way to add items to the queue and retrieve the oldest item when needed.

For fun I wanted to create a wrapper for the queue class so that it can be used in a powershell style syntax. Additionally i missed functionality to see the rate and velocity that items were added and removed to the queue.

Queue objects created with this module contains metrics for count of added and removed items, rate of added and removed items and velocity.

Repo
Docs
PowerShell Gallery

Pester, mock remote or unavailable cmdlets

· 2 min read
Hannes Palmquist
Senior Consultant Cloud

There are a lot of forum post of people having a hard time mocking some cmdlets, either it is remote cmdlets imported by a session that is not available during testing, cmdlets that are not present on the development machine or simply some built in cmdlets that are hard to mock like Import-PSSession, Invoke-Command etc.

Mocking a cmdlet that are unavailable on the development machine is quite straight forward by just declaring a dummy function before the mock.

function Get-Foo {
$Mailboxes = Get-Mailbox
}

Describe -Name "Foo" -Fixture {
BeforeAll {
function Get-Mailbox {}
Mock Get-Mailbox -MockWith {
# return fake mailbox object
}
}
It -Name "Bar" -Test {
{ Get-Foo } | should -not -throw
}
}

The same method can be applied when mocking a built in cmdlet that will fail parameter validation which happends even though the cmdlets is mocked

function Get-Foo {
$Session = New-PSSession
Import-PSSession -Session $Session
}

Describe -Name "Foo" -Fixture {
BeforeAll {
Mock New-PSSession -MockWith {}
Mock Import-PSSession -MockWith {}
}
It -Name "Bar" -Test {
{ Get-Foo } | should -not -throw
}
}
[-] Foo.Bar 51ms (49ms|1ms)
Expected no exception to be thrown, but an exception "Cannot validate argument on parameter 'Session'. The argument is null. Provide a valid value for the argument, and then try running the command again." was thrown from C:\Users\hanpalmq\OneDrive - Crayon Group\Desktop\temp.ps1:3 char:31
+ Import-PSSession -Session $Session
+ ~~~~~~~~.
at { Get-Foo } | should -not -throw, C:\Users\hanpalmq\OneDrive - Crayon Group\Desktop\temp.ps1:12
at <ScriptBlock>, C:\Users\hanpalmq\OneDrive - Crayon Group\Desktop\temp.ps1:12
Tests completed in 214ms
Tests Passed: 0, Failed: 1, Skipped: 0 NotRun: 0

The error states that Import-PSSession requires valid PSSession object to be passed to the session parameter. Again we need to define a dummy function that don't expects a PSSession object to be passed.

function Get-Foo {
$Session = New-PSSession
Import-PSSession -Session $Session
}

Describe -Name "Foo" -Fixture {
BeforeAll {
function Import-PSSession {}
Mock New-PSSession -MockWith {}
Mock Import-PSSession -MockWith {}
}
It -Name "Bar" -Test {
{ Get-Foo } | should -not -throw
}
}

Convert-DateStringToDateTimeObject

· 2 min read
Hannes Palmquist
Senior Consultant Cloud

Eventually you will stumble upon badly formatted date/time strings that you need to parse. Instead of doing a lot of Split, Trim, Substring and what not you can leverage the DateTime class methods Parse/TryParse/ParseExact. To PS-ify the use of this method I wrote this powershell function that can take any date/time string and convert it to either a DateTime object or a new string format.

function Convert-DateStringToDateTimeObject {
<#
.DESCRIPTION
Parses a datetimestring with a defined pattern to a datetime object
.PARAMETER DateString
Defines the string to parse
.PARAMETER PatternIn
Defines the pattern that datestring is formatted in
.PARAMETER PatternOut
Optional. If this parameter is omitted a standard datetime object is
returned. It is however possible to define an output pattern where
the datetime object is converted back to a string but with the output
pattern instead.
.PARAMETER Culture
Defines to culture to use for conversion. Default is console default ($PSCulture)
.EXAMPLE
$InputString = '2018_06_11_11_05_03'
Convert-DateStringToDateTimeObject -DateString $InputString -PatternIn
'yyyy_MM_dd_HH_mm_ss' -PatternOut 'yyyy-MM-dd HH:mm:ss'
Convert the string date time representation '2018_06_11_11_05_03' to a
valid datetime object and formats that datetime object to a new string format.
.NOTES
AUTHOR Hannes Palmquist
AUTHOREMAIL [email protected]
COPYRIGHT © 2019, Hannes Palmquist, All Rights Reserved
#>
param(
[Parameter(Mandatory)][string]$DateString,
[Parameter(Mandatory)][string]$PatternIn,
[string]$PatternOut = '',
[string]$Culture = $PSCulture
)

$DateTimeFormat = [cultureinfo]::GetCultureInfo($Culture).DateTimeFormat

$DateTimeObject = [DateTime]::ParseExact($DateString, $PatternIn, $DateTimeFormat)

if ($PatternOut -eq '') {
Write-Output $DateTimeObject
} else {
Write-Output $DateTimeObject.ToString($PatternOut)
}
}

So if we have a date/time string like “2018_06_11_11_05_03” we can convert that date time to a date time object by writing:

Convert-DateTimeStringToDateTimeObject -InputString "2018_06_11_11_05_03" -PatternIn "yyyy_MM_dd_HH_mm_ss"
11 juni 2018 11:05:03

You can also use the parameter “PatternOut” to set a specific format to return the DateTime object as.

Note that “\” (backslash) and “:” (semi-colon) needs to be escaped.

Get-WeekInfo

· 3 min read
Hannes Palmquist
Senior Consultant Cloud

In some parts of the world it is more common to work with weeks as measurement of time. Unfortunately there are not easy accessible ways to work with weeks in powershell or .NET. There is some support of retrieving a week number with the culture datatype however if you have a week number and want to resolve dates relatated to that week number you have to resolve that manually. Here is one example of how to do it.

First we define a supporting function that simply retreives the week number for a given date from the Gregorian calendar.

function Get-CalendarWeek {
<#
.DESCRIPTION
Gets the current week number based on a specific culture and it's week number descision rules.
.PARAMETER Date
Defines the date at which to return the week number for. Defaults to the current date.
.PARAMETER CultureInfo
Defines the culture that should be used to calculate the week number. Defaults to se-SV.
.EXAMPLE
Get-CalendarWeek
Get the week number for the current date.
.EXAMPLE
Get-CalendarWeek -Date 2018-01-25 -CultureInfo en-US
Get the week number for the date 2018-01-25 according to the week number calculation rules of the en-US culture.
.NOTES
Author: Hannes Palmquist
AuthorEmail: [email protected]
COPYRIGHT: © 2019, Hannes Palmquist, All Rights Reserved
#>
param(
[datetime]$Date = (Get-Date),
[string]$CultureInfo = $PSCulture
)

# Get specific culture object
$Culture = [cultureinfo]::GetCultureInfo($CultureInfo)

# retrieve calendar week
write-output $Culture.Calendar.GetWeekOfYear($Date, $Culture.DateTimeFormat.CalendarWeekRule, $Culture.DateTimeFormat.FirstDayOfWeek)
}

When we have that function we can define the function that can resolve the week.

function Get-WeekInfo {
<#
.DESCRIPTION
Gets info about a specific week
.PARAMETER Week
Defines the week number to query
.PARAMETER Year
Defines which year to query
.EXAMPLE
Get-WeekInfo -Week 5 -Year 1988
Gets the first date of the fifth week of 1988
.NOTES
Author: Hannes Palmquist
AuthorEmail: [email protected]
Copyright: (c) 2019, Hannes Palmquist, All Rights Reserved
#>

[CmdletBinding()] # Enabled advanced function support
param(
[Parameter(Mandatory)][ValidateRange(1, 53)][int]$Week,
[Parameter(Mandatory)][ValidateRange(1600, 2100)][int]$Year
)

BEGIN {
$WeekHash = [ordered]@{
Week = $Week
Year = $Year
}
}

PROCESS {
$ReferenceDate = Get-Date -Year $Year -Month 02 -Date 05
$ReferenceWeek = Get-CalendarWeek -Date $ReferenceDate
$WeeksDiff = $Week - $ReferenceWeek
$DateInWeek = $ReferenceDate.AddDays($WeeksDiff * 7)
$WeekHash.FirstDateOfWeek = $DateInWeek.AddDays(1 - [int]$DateInWeek.DayOfWeek)
$WeekHash.LastDateOfWeek = $WeekHash.FirstDateOfWeek.AddDays(7).AddMilliseconds(-1)
$WeekHash.StartsInMonth = ([cultureinfo]::GetCultureInfo($PSCulture)).DateTimeFormat.MonthNames[($WeekHash.FirstDateOfWeek).Month-1]
$WeekHash.EndsInMonth = ([cultureinfo]::GetCultureInfo($PSCulture)).DateTimeFormat.MonthNames[($WeekHash.LastDateOfWeek).Month-1]
}

END {
Write-Output ([pscustomobject]$WeekHash)
}
}

This will allow us to perform queries like:

Get-WeekInfo -Week 30 -Year 2018

Week : 30
Year : 2018
FirstDateOfWeek : 2018-07-23 00:00:00
LastDateOfWeek : 2018-07-29 23:59:59
StartsInMonth : July
EndsInMonth : July