Create New Outlook 365 Email in PowerShell - powershell

I'm trying to create an Outlook email in PowerShell, and I've found the same code everywhere to do it:
$ol = New-Object -comObject Outlook.Application
$mail = $ol.CreateItem(0)
$mail.Subject = "<Subject>"
$mail.Body = "<Body>"
and then either
$inspector = $mail.GetInspector
$inspector.Display()
Or
$mail.Display()
to show the email.
However, at the very first line I get this error:
New-Object : Retrieving the COM class factory for component with CLSID {0006F03A-0000-0000-C000-000000000046} failed due to the following error: 80080005 Server execution failed (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE)).
I've tried it without the -comObject and get a different error:
New-Object : Cannot find type [Outlook.Application]: verify that the assembly containing this type is loaded.
I've tried loading the assembly with
[Reflection.Assembly]::LoadWithPartialname("Microsoft.Office.Interop.Outlook") | out-null
but still get the same error messages when running the previous commands.
In case it matters, we are using Office 365, but I do have a local copy of Office installed. Is there a different type of object I need to use with Office 365?
Also, it looks like it's trying to reach out to a server to create the object. Is there a way for me to force it to do so locally?

CO_E_SERVER_EXEC_FAILURE means Outlook is running in a security context different from that of your app. COM system refuses to marshal calls between processes running in different security contexts.

Related

Powershell cannot create Excel object

I have a Win11 PC with Office365 and another Win11 PC with Office 2010
The PC with Office 2010 cannot create an Excel object in Powershell.
Here is my script:
$excel = New-Object -ComObject excel.application
$excel.Visible = $true
$workbook = $excel.Workbooks.Add(1)
$Worksheet = $workbook.worksheets.Item(1)
$ActiveWindow = $excel.ActiveWindow
Works fine on the Office365 machine, creates and opens a spreadsheet.
On the Office 2010 machine I get this error:
Exception setting "Visible": "Unable to cast COM object of type 'Microsoft.Office.Interop.Excel.ApplicationClass' to interface type
'Microsoft.Office.Interop.Excel._Application'. This operation failed because the QueryInterface call on the COM component for the
interface with IID '{000208D5-0000-0000-C000-000000000046}' failed due to the following error: Library not registered. (Exception
from HRESULT: 0x8002801D (TYPE_E_LIBNOTREGISTERED))."
At line:2 char:1
I am missing something obvious which has got me baffled.
Any ideas or suggestions.
The Office2010 PC has been repaired using a recovery USB to fix other issues. The trial version of Office365 was uninstalled and Office 2010 installed

How to send email by using MailKit via PowerShell?

I am having difficulty creating the Message object using mailkit so I can add the From, To, Subject and body with message that may have attachments to be send before disconnecting. I have created the MailKit object, Cancelation Token object. I am able to load the MailKit and MimeKit dlls using the add-type -path and I am able to connect, authenticate, Disconnect and Dispose but the message object creation part and sending is where I need help with. I have the following:
$MkSmtp = New-Object MailKit.Net.smtp.SmtpClient
$CanToken = New-Object System.Threading.CancellationToken ($false)
$SSL = [MailKit.Security.SecureSocketOptions]::SslOnConnect
$MkSmtp.Connect($MailServer, $Port, $SSL, $CanToken)
$MkSmtp.Authenticate(([System.Text.Encoding]::UTF8), $Username, $Password, $CanToken)
#$Message = New-Object ????
$MkSmtp.Send($Message)
$MkSmtp.Disconnect($true)
$MKSmtp.Dispose()
I thought the answer was:
$Message = [Mimekit.MimeMessage]::new()
$Message.From.Add($From)
$Message.To.Add($To)
$Message.Subject = "Test"
$TextPart = [MimeKit.TextPart]::new("plain")
$Body = "This is just a test"
$TextPart.Text = $Body
$Message.Body = $TextPart
Which came from https://www.powershellgallery.com/packages/PSGSuite/2.13.2/Content/Private%5CNew-MimeMessage.ps1. This worked on my developer machine but putting it to production yields the following error:
Method invocation failed because [MimeKit.MimeMessage] does not contain a method named 'new'
When it tries to go passed the following line:
$Message = [Mimekit.MimeMessage]::new()
Placing a breakpoint at this line after loading the dll via add-type and running "[MimeKit.MimeMessage] | Get-Member -Static" on the powershell command line on the development and production server it shows the following:
Development
Production
Same DLL is loaded on both the Development and Production. I have loaded the DLL using both Add-Type and System.Reflection.Assembly. Production is on PowerShell 4.0 while my development box is on PowerShell 5.1. Don't know if that is the issue. Anyone have any ideas?
Updated our live server to WIndows Management Framework 5.1 (PowerShell) and now has the New Method when viewing the Static Get-Member.
$Message = New-Object Mimekit.MimeMessage

Send email with Powershell from different mailbox

I have created a small PS script to create an email for my pipeline to send out whenever there is a deployment. the problem is i dont want the email to be sent from my personal email but from the company outlook email. i searched and saw different SMTP server names and using mail.from but i cant get it to work. can someone help me out?
param(
[Parameter(Mandatory=$true,Position=0)]
[string]$Address1,
[Parameter(Mandatory=$true,Position=1)]
[string]$Address2,
[switch]$Recurse,
[switch]$Force
)
$ol = New-Object -comObject Outlook.Application
$mail = $ol.CreateItem(0)
$Mail.Recipients.Add($Address1)
$Mail.Recipients.Add($Address2)
$Mail.Subject = "DSC Deployment in Progress"
$Mail.Body = "There is a DSC install beginning. . ."
$Mail.Send()
You need to assign a value to the SendUsingAccount property. The account can be found in the (outlook).Session.Accounts collection.
$sendSmtpAddress = "some.name#somedomain.com"
$account = $ol.session.acounts | ? { $_.smtpAddress -eq $sendSmtpAddress }
then, assign to the SendUsingAccount property before sending
$mail.SendUsingAccount = $account
$mail.Send()
Full example
$sendSmtpAddress = "some.name#somedomain.com"
$ol = new-object -comobject "outlook.application"
$account = $ol.session.accounts | ? { $_.smtpAddress -eq $sendSmtpAddress }
$mail = $ol.CreateItem(0)
$mail.recipients.add("target.user#somedomain.com") | out-null
$mail.subject = "test email"
$mail.body = "test email"
$mail.SendUsingAccount = $account
$mail.Send()
For what it's worth, I gave up trying to send email via Outlook a long time ago, it's much easier to use plain SMTP. Depending on the security policy on your local SMTP server (Exchange?), you may be able to 'send as' any user on your local domain. Ask your IT people for the name/IP of an internal SMTP server that you can use to send email, and then it's as easy as:
send-mailmessage -smtpServer (servername or IP) -from sender.name#domain.com -to #(recipient1#domain.com, recipient2#domain.com) -subject "Email Subject" -body "Email Body"
If using send-mailmessage, it's possible to set a Display Name for the sender by using the form "Display Name <sender.name#domain.com>" e.g.
-from "Deployment Alerts <sender.name#domain.com>"
Recipients will see the Display Name in their email client, rather than the SMTP address.
A couple of points that I consider to be good practice:
Depending on the config of the SMTP server, there may be little, or no verification of the 'sender' address. It is worth using a genuine account that you have access to, so that you have sight of any bounce / non-delivery reports.
Consider including something in the mail body (perhaps a footer) that mentions where the alert came from and what process generated it. This can help your successor or colleague track down the script in the future.
The assignment like
$mail.SendUsingAccount = $account
worked for me since Windows Server 2000 till Windows Server 2008 and from Outlook 2007 till Outlook 2016. Since Windows Server 2016 I've got an exception (The server threw an exception. (Exception from HRESULT: 0x80010105 (RPC_E_SERVERFAULT))).
But there is an alternative way to assign this property.
[Void] $mail.GetType().InvokeMember("SendUsingAccount","SetProperty",$NULL,$mail,$account)

How to run Powershell script on local computer but with credentials of a domain user

I have to implement a solution where I have to deploy a SSIS project (xy.ispac) from one machine to another. So far I've managed to copy-cut-paste the following stuff from all around the internet:
# Variables
$ServerName = "target"
$SSISCatalog = "SSISDB" # sort of constant
$CatalogPwd = "catalog_password"
$ProjectFilePath = "D:\Projects_to_depoly\Project_1.ispac"
$ProjectName = "Project_name"
$FolderName = "Data_collector"
# Load the IntegrationServices Assembly
[Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.IntegrationServices")
# Store the IntegrationServices Assembly namespace to avoid typing it every time
$ISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"
Write-Host "Connecting to server ..."
# Create a connection to the server
$sqlConnectionString = "Data Source=$ServerName;Initial Catalog=master;Integrated Security=SSPI;"
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection $sqlConnectionString
$integrationServices = New-Object "$ISNamespace.IntegrationServices" $sqlConnection
$catalog = $integrationServices.Catalogs[$SSISCatalog]
# Create the Integration Services object if it does not exist
if (!$catalog) {
# Provision a new SSIS Catalog
Write-Host "Creating SSIS Catalog ..."
$catalog = New-Object "$ISNamespace.Catalog" ($integrationServices, $SSISCatalog, $CatalogPwd)
$catalog.Create()
}
$folder = $catalog.Folders[$FolderName]
if (!$folder)
{
#Create a folder in SSISDB
Write-Host "Creating Folder ..."
$folder = New-Object "$ISNamespace.CatalogFolder" ($catalog, $FolderName, $FolderName)
$folder.Create()
}
# Read the project file, and deploy it to the folder
Write-Host "Deploying Project ..."
[byte[]] $projectFile = [System.IO.File]::ReadAllBytes($ProjectFilePath)
$folder.DeployProject($ProjectName, $projectFile)
This seemed to be working surprisingly well on the development machine - test server pair. However, the live environment will be a bit different, the machine doing the deployment job (deployment server, or DS from now on) and the SQL Server (DB for short) the project is to be deployed are in different domains and since SSIS requires windows authentication, I'm going to need to run the above code locally on DS but using credentials of a user on the DB.
And that's the point where I fail. The only thing that worked is to start the Powershell command line interface using runas /netonly /user:thatdomain\anuserthere powershell, enter the password, and paste the script unaltered into it. Alas, this is not an option, since there's no way to pass the password to runas (at least once with /savecred) and user interactivity is not possible anyway (the whole thing has to be automated).
I've tried the following:
Simply unning the script on DS, the line $sqlConnection = New-Object System.Data.SqlClient.SqlConnection $sqlConnectionString would use the credentials from DS which is not recognized by DB, and New-Object does not have a -Credential arg that I could pass to
Putting everything into an Invoke-Command with -Credential requires using -Computername as well. I guess it would be possible to use the local as 'remote' (using . as Computername) but it still complains about access being denied. I'm scanning through about_Remote_Troubleshooting, so far without any success.
Any hints on how to overcome this issue?
A solution might be to use a sql user (with the right access rights) instead of an AD used.
Something like this should work.
(Check also the answer to correct the connection string)

Remote Powershell Popup message to all users not working

I'm having some issues creating a remote powershell popup message.
I've got a working script that displays the popup message that I want to send to a remote user. However, whenever I bundle it up in a .ps1 script and run it remotely it does not send a popup message to the logged in user. I know the script is running correctly, as I have other parts of the script that execute correctly. I was able to run the popup message on a local machine, so it is not a script error.
The script is:
Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object system.Windows.Forms.Form
$Form.Text = 'ALERT!'
$form.ControlBox = $false;
$Image = [system.drawing.image]::FromFile('\\filepath')
$Form.BackgroundImage = $Image
$Form.BackgroundImageLayout = 'Stretch'
$Form.Width = (680)
$Form.Height = (550)
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(500,445)
$OKButton.Size = New-Object System.Drawing.Size(100,50)
$OKButton.Text = 'Accept'
$OKButton.Font = New-Object System.Drawing.Font('Times New Roman',18)
$OKButton.Add_Click({$Form.Close()})
$Form.Controls.Add($OKButton)
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
I'm running this as part of a .ps1 that is called in the following fashion:
Invoke-WmiMethod -Class Win32_Process -ComputerName $computer -Name Create -ArgumentList "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe \\$computer\C$\folder\script.ps1
I'm running the script as a service account from a server that's executing the commands on a Windows 7 enterprise desktop machine. The target machine execution policy is set to unrestricted so I know it is not a script execution policy issue.
I have a hunch that the reason that it is not popping up on the target machine is because the account that is running the script and the account that is logged into the target machine are different, however I could be incorrect.
Your hunch is correct. The PowerShell script is running in the context of the service account, and won't show up for any of the logged in users. (for ex. if your script launched notepad.exe, it would run only for the service account).
You will see it pop up when you run the command locally under the same account.
#Adrian R is correct you can use msg.exe to send a message, or the more commandline friendly:
net send /users message
If you want to show something more complex (i.e. a full-on Windows form) instead of the classic message box, then you need to be running something in the user context. One way to do this is to use PsExec with the -i (interactive option) instead of using PowerShell remoting.