diff --git a/classlib/Cmdlets/BaseCmdlet.cs b/classlib/Cmdlets/BaseCmdlet.cs
new file mode 100644
index 0000000..83dc9d3
--- /dev/null
+++ b/classlib/Cmdlets/BaseCmdlet.cs
@@ -0,0 +1,114 @@
+namespace PS.IPAM.Cmdlets;
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Management.Automation;
+using PS.IPAM.Helpers;
+
+///
+/// Base class for all PS.IPAM cmdlets providing common functionality.
+///
+public abstract class BaseCmdlet : PSCmdlet
+{
+ ///
+ /// Writes the result to the pipeline, handling both single objects and collections.
+ ///
+ /// The result to write to the pipeline.
+ protected void WriteResult(object? result)
+ {
+ if (result == null)
+ {
+ return;
+ }
+
+ if (result is IEnumerable enumerable && result is not string)
+ {
+ foreach (var item in enumerable)
+ {
+ WriteObject(item);
+ }
+ }
+ else
+ {
+ WriteObject(result);
+ }
+ }
+
+ ///
+ /// Converts custom fields from various formats to a dictionary.
+ ///
+ /// The custom fields object to convert.
+ /// A dictionary of custom field names and values.
+ protected static Dictionary ConvertCustomFields(object? customFields)
+ {
+ if (customFields == null)
+ {
+ return new Dictionary();
+ }
+
+ if (customFields is PSObject psobj)
+ {
+ var dict = new Dictionary();
+ foreach (var prop in psobj.Properties)
+ {
+ dict[prop.Name] = prop.Value ?? string.Empty;
+ }
+ return dict;
+ }
+
+ if (customFields is Dictionary dictObj)
+ {
+ return dictObj;
+ }
+
+ if (customFields is IDictionary genericDict)
+ {
+ var result = new Dictionary();
+ foreach (DictionaryEntry entry in genericDict)
+ {
+ result[entry.Key?.ToString() ?? string.Empty] = entry.Value ?? string.Empty;
+ }
+ return result;
+ }
+
+ return new Dictionary();
+ }
+
+ ///
+ /// Executes an async operation synchronously and handles errors.
+ ///
+ /// The return type of the operation.
+ /// The async operation to execute.
+ /// The error ID for error reporting.
+ /// The result of the operation, or default if an error occurred.
+ protected T? ExecuteAsync(Func> operation, string errorId)
+ {
+ try
+ {
+ return operation().GetAwaiter().GetResult();
+ }
+ catch (Exception ex)
+ {
+ WriteError(new ErrorRecord(ex, errorId, ErrorCategory.InvalidOperation, null));
+ return default;
+ }
+ }
+
+ ///
+ /// Executes an async operation synchronously and handles errors.
+ ///
+ /// The async operation to execute.
+ /// The error ID for error reporting.
+ protected void ExecuteAsync(Func operation, string errorId)
+ {
+ try
+ {
+ operation().GetAwaiter().GetResult();
+ }
+ catch (Exception ex)
+ {
+ WriteError(new ErrorRecord(ex, errorId, ErrorCategory.InvalidOperation, null));
+ }
+ }
+}
diff --git a/classlib/IsExternalInit.cs b/classlib/IsExternalInit.cs
new file mode 100644
index 0000000..85ba17f
--- /dev/null
+++ b/classlib/IsExternalInit.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// This file provides the IsExternalInit type required for using C# 9 records
+// in projects targeting .NET Standard 2.1 or earlier.
+
+#if !NET5_0_OR_GREATER
+
+namespace System.Runtime.CompilerServices
+{
+ using System.ComponentModel;
+
+ ///
+ /// Reserved to be used by the compiler for tracking metadata.
+ /// This class should not be used by developers in source code.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal static class IsExternalInit
+ {
+ }
+}
+
+#endif
diff --git a/classlib/enum/SessionStatus.cs b/classlib/enum/SessionStatus.cs
new file mode 100644
index 0000000..0c9c73c
--- /dev/null
+++ b/classlib/enum/SessionStatus.cs
@@ -0,0 +1,22 @@
+namespace PS.IPAM;
+
+///
+/// Represents the current status of an API session.
+///
+public enum SessionStatus
+{
+ ///
+ /// No session exists or no token is available.
+ ///
+ NoSession,
+
+ ///
+ /// The session token has expired.
+ ///
+ Expired,
+
+ ///
+ /// The session is valid and ready for use.
+ ///
+ Valid
+}