Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# Changelog for ScriptMessage PowerShell Module

## [1.1.0](https://github.com/Sekers/ScriptMessage/tree/1.1.0) - (2025-10-16)

### Fixes

- ScriptMessage now properly allows for for NULL in TO, CC, BCC parameters in Microsoft Graph.
- Reverted recently added code formatting that included Begin\End process blocks as the way they were implemented caused bugs.
- Catch if a Teams Chat attachments folder does not already exists in the user's OneDrive.
- The 'AllowableMessageTypes' Microsoft Graph configuration is now being applied properly.

### Features

- Much better error handling with the Microsoft Graph service. Warnings and Errors are now collected and if one service type has an error the command will still process the other service type (when applicable).
- Get-ScriptMessageConfig can now optionally pull data for a specific service only. Returning all services configurations is still the default.
- Sending multiple attachments in a single email or chat now works with Microsoft Graph.
- Sending attachments with non-standard filenames (uploaded to the user's OneDrive) using Microsoft Graph Chat is now supported.

### Other

- Changes made to only import necessary Microsoft Graph PowerShell modules.

Author: [**@Sekers**](https://github.com/Sekers)

---
## [1.0.8](https://github.com/Sekers/ScriptMessage/tree/1.0.8) - (2025-09-18)

### Fixes
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

- [This Module is in Public Preview](#this-module-is-in-public-preview)
- [Overview](#overview)
- [Currently Supported Services](#currently-supported-services)
- [Supported Services](#supported-services)
- [What's New](#whats-new)
- [Documentation](#documentation)
- [Developing and Contributing](#developing-and-contributing)
Expand All @@ -23,7 +23,7 @@ ScriptMessage is designed to simplify the use of messaging services in PowerShel
- Specify more than one service in a single command to easily send the same message multiple ways for redundancy or other purposes. For example, you might want to send an email using Microsoft Graph and a chat message using both Teams & Slack for the same alert.
- Easily switch the desired messaging service(s) in your scripts by updating simple config files. Whether your current messaging service is deprecated, you need to add a new service, or switch to a different service, you will no longer need to rewrite all of your scripts.

## Currently Supported Services
## Supported Services

- [**Microsoft Graph SDK PowerShell:**](https://learn.microsoft.com/en-us/powershell/microsoftgraph/overview?view=graph-powershell-1.0) Take advantage of the Microsoft Graph SDK PowerShell module to send email and chat messages using the Graph API without having to learn all the object formatting that the API requires (which unfortunately the SDK doesn't simplify).
- Since the Microsoft Graph API only supports Teams Chat when using delegated [permissions](https://learn.microsoft.com/en-us/graph/permissions-overview), we are looking into [Teams Bots](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/overview) support for future releases to allow for application permissions.
Expand Down
109 changes: 50 additions & 59 deletions ScriptMessage/Private/ConvertTo-ScriptMessageAttachmentObject.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,73 +10,64 @@ Function ConvertTo-ScriptMessageAttachmentObject
[array]$Attachment
)

begin
if ([string]::IsNullOrEmpty($Attachment))
{
if ([string]::IsNullOrEmpty($Attachment))
{
return $null
}
return $null
}

process
{
[array]$ScriptMessageAttachment = foreach ($currentAttachment in $Attachment)
{
switch ($currentAttachment.GetType().Name)
{
'Hashtable' { # If direct file content is supplied.
$AttachmentType = 'Content'
if (($currentAttachment.ContainsKey('Name')) -and $currentAttachment.ContainsKey('Content'))
{
[PSCustomObject]$ScriptMessageAttachmentItem = @{
Name = $currentAttachment.Name
Content = $currentAttachment.Content
}
}
else
{
throw "The attachment hashtable object is improperly formatted. The hashtable requires the keys of `'Name`' and `'Content`'"

[array]$ScriptMessageAttachment = foreach ($currentAttachment in $Attachment)
{
switch ($currentAttachment.GetType().Name)
{
'Hashtable' { # If direct file content is supplied.
$AttachmentType = 'Content'
if (($currentAttachment.ContainsKey('Name')) -and $currentAttachment.ContainsKey('Content'))
{
[PSCustomObject]$ScriptMessageAttachmentItem = @{
Name = $currentAttachment.Name
Content = $currentAttachment.Content
}
}
'String' { # If a directory or file path is supplied.
if (-not (Test-Path -Path $currentAttachment))
{
throw 'Invalid path to attachment directory or file.'
else
{
throw "The attachment hashtable object is improperly formatted. The hashtable requires the keys of `'Name`' and `'Content`'"
}
}
'String' { # If a directory or file path is supplied.
if (-not (Test-Path -Path $currentAttachment))
{
throw 'Invalid path to attachment directory or file.'
}

switch ((Get-Item -Path $currentAttachment).GetType().Name)
{
'FileInfo'{
$AttachmentType = 'FilePath'
$FileInfo = Get-Item -Path $currentAttachment
[PSCustomObject]$ScriptMessageAttachmentItem = @{
Name = $FileInfo.Name
Content = [System.IO.File]::ReadAllBytes($FileInfo.FullName)
}
}

switch ((Get-Item -Path $currentAttachment).GetType().Name)
{
'FileInfo'{
$AttachmentType = 'FilePath'
$FileInfo = Get-Item -Path $currentAttachment
[PSCustomObject]$ScriptMessageAttachmentItem = @{
Name = $FileInfo.Name
Content = [System.IO.File]::ReadAllBytes($FileInfo.FullName)
'DirectoryInfo' {
$AttachmentType = 'DirectoryPath'
$DirectoryContent = Get-ChildItem $currentAttachment -File -Recurse
[PSCustomObject]$ScriptMessageAttachmentItem = foreach ($file in $DirectoryContent)
{
@{
Name = $file.Name
Content = [System.IO.File]::ReadAllBytes($file.FullName)
}
}
'DirectoryInfo' {
$AttachmentType = 'DirectoryPath'
$DirectoryContent = Get-ChildItem $currentAttachment -File -Recurse
[PSCustomObject]$ScriptMessageAttachmentItem = foreach ($file in $DirectoryContent)
{
@{
Name = $file.Name
Content = [System.IO.File]::ReadAllBytes($file.FullName)
}
}
}
Default {throw 'Unexpected attachment object type.'}
}
Default {throw 'Unexpected attachment object type.'}
}
Default {throw 'Unexpected attachment object type.'}
}

$ScriptMessageAttachmentItem
}
}

end
{
return $ScriptMessageAttachment
}
Default {throw 'Unexpected attachment object type.'}
}

$ScriptMessageAttachmentItem
}

return $ScriptMessageAttachment
}
37 changes: 14 additions & 23 deletions ScriptMessage/Private/ConvertTo-ScriptMessageBodyObject.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,26 @@ function ConvertTo-ScriptMessageBodyObject
[pscustomobject]$Body
)

begin
if ([string]::IsNullOrEmpty($Body))
{
if ([string]::IsNullOrEmpty($Body))
{
return $null
}
return $null
}

process
# Check if 'Body' is string. If it is, turn into a PSobject.
if ($Body.GetType().Name -eq 'String')
{
# Check if 'Body' is string. If it is, turn into a PSobject.
if ($Body.GetType().Name -eq 'String')
{
$ScriptMessageBodyObject = [PSCustomObject]@{
ContentType = 'Text'
Content = $Body
}
}
else # Return item as properly formatted PSObject that includes the 'ContentType' property.
{
$ScriptMessageBodyObject = [PSCustomObject]@{
ContentType = $Body.ContentType
Content = $Body.Content
}
$ScriptMessageBodyObject = [PSCustomObject]@{
ContentType = 'Text'
Content = $Body
}
}

end
else # Return item as properly formatted PSObject that includes the 'ContentType' property.
{
return $ScriptMessageBodyObject
$ScriptMessageBodyObject = [PSCustomObject]@{
ContentType = $Body.ContentType
Content = $Body.Content
}
}

return $ScriptMessageBodyObject
}
48 changes: 19 additions & 29 deletions ScriptMessage/Private/ConvertTo-ScriptMessageRecipientObject.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,37 @@ function ConvertTo-ScriptMessageRecipientObject
[pscustomobject]$Recipient
)


begin
if (([string]::IsNullOrEmpty($Recipient)) -and ($Recipient.Count -lt 1))
{
if (([string]::IsNullOrEmpty($Recipient)) -and ($Recipient.Count -lt 1))
{
return $null
}
return $null
}

process
[array]$ScriptMessageRecipientObject = foreach ($recipientItem in $Recipient)
{
[array]$ScriptMessageRecipientObject = foreach ($recipientItem in $Recipient)
# Check if 'recipientItem' is string (email address, etc.). If it is, turn into a PSobject.
if ($recipientItem.GetType().Name -eq 'String')
{
[PSCustomObject]@{
AddressObj = $recipientItem # Don't use 'Address' because it can conflict with the 'Address()' method.
}
}
else # Return item as properly formatted PSObject that includes the 'Name' property.
{
# Check if 'recipientItem' is string (email address, etc.). If it is, turn into a PSobject.
if ($recipientItem.GetType().Name -eq 'String')
if ([string]::IsNullOrEmpty($recipientItem.Name))
{
[PSCustomObject]@{
AddressObj = $recipientItem # Don't use 'Address' because it can conflict with the 'Address()' method.
AddressObj = $recipientItem.Address # Don't use 'Address' because it can conflict with the 'Address()' method.
}
}
else # Return item as properly formatted PSObject that includes the 'Name' property.
else
{
if ([string]::IsNullOrEmpty($recipientItem.Name))
{
[PSCustomObject]@{
AddressObj = $recipientItem.Address # Don't use 'Address' because it can conflict with the 'Address()' method.
}
}
else
{
[PSCustomObject]@{
Name = $recipientItem.Name
AddressObj = $recipientItem.Address # Don't use 'Address' because it can conflict with the 'Address()' method.
}
[PSCustomObject]@{
Name = $recipientItem.Name
AddressObj = $recipientItem.Address # Don't use 'Address' because it can conflict with the 'Address()' method.
}
}
}
}

end
{
return $ScriptMessageRecipientObject
}

return $ScriptMessageRecipientObject
}
42 changes: 15 additions & 27 deletions ScriptMessage/Public/Connect-ScriptMessage.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ function Connect-ScriptMessage
Returns connection information after performing function.

.EXAMPLE
Connect-ScriptMessage -Service MgGraph
Connect-ScriptMessage -Service MicrosoftGraph
.EXAMPLE
Connect-ScriptMessage -Service MgGraph -ReturnConnectionInfo
Connect-ScriptMessage -Service MicrosoftGraph -ReturnConnectionInfo
#>

[CmdletBinding()]
Expand All @@ -36,33 +36,21 @@ function Connect-ScriptMessage
ValueFromPipelineByPropertyName=$true)]
[switch]$ReturnConnectionInfo
)

begin
{
# Set the necessary configuration variables.
$ScriptMessageConfig = Get-ScriptMessageConfig

# Set the connection parameters.
$ConnectionParameters = @{
ServiceConfig = $ScriptMessageConfig.$Service
}

# Set the connection parameters.
$ConnectionParameters = @{
ServiceConfig = Get-ScriptMessageConfig -Service $Service
}

process #TODO: Can this check if already connected???

# Connect to the proper service.
switch ($Service)
{
# Connect to the proper service.
switch ($Service)
{
MgGraph {Connect-ScriptMessage_MGGraph @ConnectionParameters}
}
MicrosoftGraph {Connect-ScriptMessage_MicrosoftGraph @ConnectionParameters}
}

end
{
# Return the connection information, if requested.
if ($ReturnConnectionInfo)
{
return Get-ScriptMessageContext -Service $Service
}

# Return the connection information, if requested.
if ($ReturnConnectionInfo)
{
return Get-ScriptMessageContext -Service $Service
}
}
Loading