Image not displayed in the mail using powershell script [duplicate] - sql-server

I am editing one of my old scripts to send an email to a user with images embedded into the text. I am trying to use the Send-MailMessage function to send the email as opposed to the older method of $smtp.send($msg). However, when trying to update the script, the images are no longer being embedded.
I know how to attach them to the email as actual attachments, but I am not sure what I am doing wrong to have them show as actual embedded images.
NOTE: for brevity, I removed some of the full email since it is large and as long as I can get an image or two working, it will all work.
# force powershell to run as an x86 process
Set-ExecutionPolicy -Scope CurrentUser Unrestricted
if ($env:Processor_Architecture -ne "x86") {
&"$env:windir\syswow64\windowspowershell\v1.0\powershell.exe" -file $myinvocation.Mycommand.path
exit
}
# initialize the script
if ($startupvariables) { try {Remove-Variable -Name startupvariables -Scope Global -ErrorAction SilentlyContinue } catch { } }
New-Variable -force -name startupVariables -value ( Get-Variable | ForEach-Object { $_.Name } )
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$Separator = ".", "#"
# advise what the script does
Add-Type -AssemblyName PresentationCore,PresentationFramework
$ButtonType = [System.Windows.MessageBoxButton]::OKCancel
$MessageIcon = [System.Windows.MessageBoxImage]::Warning
$MessageTitle = "Shared Drive Access Assistance"
$MessageBody = "This script asks the user to provide more information regarding a network drive that they would like access to.`n`nTo use it, enter the below information:`n`n`n`tTicket Number`n`n`tUser's Email Address`n`n`tRequestor's Email Address`n`n`nIf this is the script you want to use, click OK.`nIf not, click Cancel."
$Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
if ($Result -eq "Cancel")
{
Exit-PSSession
}
else
{
# get the ticket number
$Ticket = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the SCTask ticket number" , "Ticket Number")
# get the user id via the email address
$UserID = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the user's email address" , "User Email Address")
$User = $UserID.split($Separator)
$Firstname = $User[0].substring(0,1).toupper()+$User[0].substring(1).tolower()
$Lastname = $User[1].substring(0,1).toupper()+$User[1].substring(1).tolower()
$User = $Firstname, $Lastname
# get local username
$Username = [System.Environment]::UserName
# create email
$subject = "Ticket $Ticket on Hold for User Response - Shared Drive Access for $User - Awaiting Additional Information"
$body = #"
<html>
<body style="font-family:calibri">
To $Requestor, $User,<br>
<br>
<br>
In order to proceed with your request for shared drive access, we require the server name and full path to the folder you need access to. If you do not already know this information, you will need to provide these instructions to someone that already has access to the folder that you need access to.<br>
<br>
1) Click the Start menu<br>
<br>
<img src="cid:image1.png"><br>
<img src="cid:image2.png"><br>
<img src="cid:image3.png"><br>
<br>
<br>
2) Navigate to "Computer"<br>
<br>
<img src="cid:image4.png"><br>
<br>
<br>
<br>
If you have any questions or need assistance with this process, please contact the Service Desk via one of the methods listed below.
<br>
<br>
Thank You,<br>
<br>
IT Service Desk<br>
</body>
</html>
"#
$att1 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive1.png")
$att2 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive2.png")
$att3 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive3.png")
$att4 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive4.png")
$att1.ContentId = "image1.png"
$att2.ContentId = "image2.png"
$att3.ContentId = "image3.png"
$att4.ContentId = "image4.png"
# $msg.Attachments.Add($att1)
# $msg.Attachments.Add($att2)
# $msg.Attachments.Add($att3)
# $msg.Attachments.Add($att4)
# create confirmation message
$ButtonType = [System.Windows.MessageBoxButton]::YesNo
$MessageIcon = [System.Windows.MessageBoxImage]::Warning
$MessageTitle = "Shared Drive Access Assistance"
$MessageBody = "The information you have entered is show below:`n`n`nTicket Number: $Ticket`n`nUser's Email Address: $UserID`n`nRequstor's Email Address: $RequestorID`n`n`nIf you would like to send the email, click Yes.`nOtherwise, click No."
$Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
if ($Result -eq "No")
{
Exit-PSSession
}
else
# send email
{
Send-MailMessage -To "<$UserID>" -bcc "<$Username#dana.com>" -from "<itservicedesk#x.com>" -Subject $global:subject -SmtpServer "mailrelay.x.com" -BodyAsHtml -body $global:body
}
}
Function Clean-Memory {
Get-Variable |
Where-Object { $startupVariables -notcontains $_.Name } |
ForEach-Object {
try { Remove-Variable -Name "$($_.Name)" -Force -Scope "global" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue}
catch { }
}
}

So the real question is how to embed a image into the HTML document from a attachment
CID aka Content ID will allow you to attach a image and then use that attached image in the document. Avoid using spaces in the Content ID name.
Send-MailMessage -To "Test#Test.com" -from "Test2#Test.com" -SmtpServer SMTP.TEST.NET -Subject "Hello" -BodyAsHtml -Body "<img src='cid:Test.png'>" -Port 25 -Attachments "C:\Users\Test\Test.png"
You are using
$att1 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive1.png")
$att2 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive2.png")
$att3 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive3.png")
$att4 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive4.png")
$att1.ContentId = "image1.png"
$att2.ContentId = "image2.png"
$att3.ContentId = "image3.png"
$att4.ContentId = "image4.png"
but when you send the mail you are not attaching those attachments
Send-MailMessage -To "<$UserID>" -bcc "<$Username#dana.com>" -from "<itservicedesk#x.com>" -Subject $global:subject -SmtpServer "mailrelay.x.com" -BodyAsHtml -body $global:body
You could stop using the Net.Mail.Attachment and instead do something like
$Body = #"
<html>
<body style="font-family:calibri">
<b>This is image 1</b>
<img src='cid:TEST1.png'>
<b>This is image 2</b>
<img src='cid:Test2.png'>
</body>
</html>
"#
Send-MailMessage -To "Test#Test.com" `
-from "Test2#Test.com" `
-SmtpServer Test.smtp.com `
-Subject "Hello" `
-BodyAsHtml -body $body `
-Attachments "C:\Test\TEST1.png", "C:\Test\TEST2.png"

You can embed the image into the HTML, therefore there are no extra files.
It is replacing
{img=file}
with data URL
{src="data:image/png;base64,[base64 encoded long string representing the image]}
There are many online converters that can convert your image file into the required code.
Just google for Image to Data URI converter.

Here is a simple example using MailKit. I had do download the packages from https://www.nuget.org/packages/MimeKit and https://www.nuget.org/packages/MailKit and unpack with 7Zip as PowerShell was struggling to install from the CLI. I got some ideas from here but wanted to add an example of an embedded image, https://adamtheautomator.com/powershell-email.
You can do all sorts with MimeKit and MailKit including modern Auth which is great. It's also supper fast and the Client and even a Connection can be used in a loop to send lots of messages. There is probably an upper bound on how long to use a connection without refreshing?
$mailServer = "domain-com.mail.protection.outlook.com"
$SMTP = New-Object MailKit.Net.Smtp.SmtpClient
Add-Type -Path ".\mailkit.3.3.0\netstandard2.0\MailKit.dll"
Add-Type -Path ".\mimekit.3.3.0\netstandard2.0\MimeKit.dll"
$SMTP.Connect($mailServer, 25, [MailKit.Security.SecureSocketOptions]::StartTls, $False)
$Message = New-Object MimeKit.MimeMessage
$message.From.Add("a.person#domain.com");
$message.To.Add("b.person#domain.com");
$message.Subject = "Some Subject";
$builder = [MimeKit.BodyBuilder]::new();
$image = $builder.LinkedResources.Add($path);
$image.ContentId = [MimeKit.Utils.MimeUtils]::GenerateMessageId();
$body = #"
<html>
<body style="font-family:calibri">
<b>This is image 1</b>
<img src='cid:$($image.ContentId)'>
</body>
</html>
"#
$builder.HtmlBody = $body
#Now we just need to set the message body and we're done
$message.Body = $builder.ToMessageBody();
$SMTP.Send($Message)
$SMTP.Disconnect($true)
$SMTP.Dispose()

Related

Trying to create a variable with the format operator but the data for the variable changes

So, I'm back to working on a script I was playing with a while back. It has changed a lot since its conception. But, it still basically pulls data from a CSV and creates the accounts in AD as needed. It worked pretty well but I ran into trouble when we noticed we had people without middle names. The middle initial is part of our password format. So, I've tried a few things, including recycling some of my other code, but I cannot seem to get the AD account created with the proper password. The account gets created but I can never authenticate with the creds the account should accept.
I've added what I feel is the relevant code. It's not the whole script as that is 200+ lines and seems to function fine but, if you want to see it all, let me know and I'll edit the following snippet.
The code-snippet that works so long as the kid has a middle initial:
# CSV file being imported.
$CsvFile = "$env:USERPROFILE\Downloads\SampleData.csv"
# Import the contents of the CSV file.
$Users = Import-Csv -Path "$CsvFile"
# Loop through each line of the CSV, creating the user if the account doesn't already exist in AD.
ForEach ($User in $Users) {
[String]$LoginName = $User.'Stu Access Login'
If (-Not (Get-ADUser -Server $ADServer -Filter {SamAccountName -eq $LoginName})) {
$FirstName = $User.'Student First Name'
$LastName = $User.'Student Last Name'
$ADUserParams = #{
Name = "$FirstName $LastName"
SamAccountName = $LoginName
GivenName = $FirstName
Initials = $User.'I'
Surname = $LastName
DisplayName = "$FirstName $($User.'I') $LastName"
Description= $SchoolCodes[$User.School].Name
ScriptPath= "student.bat"
UserPrincipalName = "$LoginName#academic.mydomain.k12.pa.us"
EmailAddress = "$LoginName#mydomain.org"
Company = "$LoginName#mydomain.org"
EmployeeID = $User.'Other ID'
HomeDirectory = "$FileServer\$LoginName"
HomeDrive = "Z:"
AccountPassword = ConvertTo-SecureString -String (
'{0}{1}{2}#{3}' -f #(
$FirstName[0].ToString().ToUpper(),
$User.I[0].ToString().ToLower(),
$LastName[0].ToString().ToLower(),
$User.'Other ID')) -AsPlainText -Force
Enabled = $True
PasswordNeverExpires = $True
CannotChangePassword = $True
Path = 'OU={0},OU=Students,OU={1},OU=accounts,DC=academic,DC=mydomain,DC=k12,DC=pa,DC=us' -f #(
$User.'Grad Year',
$SchoolCodes[$User.School].Name)
Server = $ADServer
WhatIf = $False
} # End ADUserParams
Try {
# Create new user.
New-ADUser #ADUserParams -Verbose -ErrorAction Stop
}
Catch {
# If there's an error, write the error to the event log.
Write-EventLog -LogName $EventLogName -Source $EventLogSources[0][1] -EntryType Warning -EventId $EventLogSources[0][0] -Message "Something went wrong with the creation of a new user, [$LoginName] : $_"
}}}
First attempt to get around the missing middle initial:
# CSV file being imported.
$CsvFile = "$env:USERPROFILE\Downloads\SampleData.csv"
# Import the contents of the CSV file.
$Users = Import-Csv -Path "$CsvFile"
# Loop through each line of the CSV, creating the user if the account doesn't already exist in AD.
ForEach ($User in $Users) {
[String]$LoginName = $User.'Stu Access Login'
If (-Not (Get-ADUser -Server $ADServer -Filter {SamAccountName -eq $LoginName})) {
# Attempt #1 for Dealing with passwords for people without a middle initial.
IF([String]::IsNullOrEmpty($User.I)) {
$AccountPass = '{0}{1}#{2}' -f #(
$FirstName[0].ToString().ToUpper(),
$LastName[0].ToString().ToLower(),
$User.'Other ID')
} Else {
$AccountPass = '{0}{1}{2}#{3}' -f #(
$FirstName[0].ToString().ToUpper(),
$User.I[0].ToString().ToLower(),
$LastName[0].ToString().ToLower(),
$User.'Other ID')
}
$FirstName = $User.'Student First Name'
$LastName = $User.'Student Last Name'
$ADUserParams = #{
Name = "$FirstName $LastName"
SamAccountName = $LoginName
GivenName = $FirstName
Initials = $User.'I'
Surname = $LastName
DisplayName = "$FirstName $($User.'I') $LastName"
Description= $SchoolCodes[$User.School].Name
ScriptPath= "student.bat"
UserPrincipalName = "$LoginName#academic.mydomain.k12.pa.us"
EmailAddress = "$LoginName#mydomain.org"
Company = "$LoginName#mydomain.org"
EmployeeID = $User.'Other ID'
HomeDirectory = "$FileServer\$LoginName"
HomeDrive = "Z:"
AccountPassword = (ConvertTo-SecureString -String $AccountPass -AsPlainText -Force)
Enabled = $True
PasswordNeverExpires = $True
CannotChangePassword = $True
Path = 'OU={0},OU=Students,OU={1},OU=accounts,DC=academic,DC=mydomain,DC=k12,DC=pa,DC=us' -f #(
$User.'Grad Year',
$SchoolCodes[$User.School].Name)
Server = $ADServer
WhatIf = $False
} # End ADUserParams
Try {
# Create new user.
New-ADUser #ADUserParams -Verbose -ErrorAction Stop
}
Catch {
# If there's an error, write the error to the event log.
Write-EventLog -LogName $EventLogName -Source $EventLogSources[0][1] -EntryType Warning -EventId $EventLogSources[0][0] -Message "Something went wrong with the creation of a new user, [$LoginName] : $_"
}}}
Second attempt to get around the missing middle initial:
# CSV file being imported.
$CsvFile = "$env:USERPROFILE\Downloads\SampleData.csv"
# Import the contents of the CSV file.
$Users = Import-Csv -Path "$CsvFile"
# Loop through each line of the CSV, creating the user if the account doesn't already exist in AD.
ForEach ($User in $Users) {
[String]$LoginName = $User.'Stu Access Login'
If (-Not (Get-ADUser -Server $ADServer -Filter {SamAccountName -eq $LoginName})) {
# Attempt #2 for Dealing with passwords for people without a middle initial.
If ($User.I -ne "") {
$AccountPass = ConvertTo-SecureString -String (
'{0}{1}{2}#{3}' -f #(
$FirstName[0].ToString().ToUpper(),
$User.I[0].ToString().ToLower(),
$LastName[0].ToString().ToLower(),
$User.'Other ID')) -AsPlainText -Force
} Else {
$AccountPass = ConvertTo-SecureString -String (
'{0}{1}#{2}' -f #(
$FirstName[0].ToString().ToUpper(),
$LastName[0].ToString().ToLower(),
$User.'Other ID')) -AsPlainText -Force
} # End If
$FirstName = $User.'Student First Name'
$LastName = $User.'Student Last Name'
$ADUserParams = #{
Name = "$FirstName $LastName"
SamAccountName = $LoginName
GivenName = $FirstName
Initials = $User.'I'
Surname = $LastName
DisplayName = "$FirstName $($User.'I') $LastName"
Description= $SchoolCodes[$User.School].Name
ScriptPath= "student.bat"
UserPrincipalName = "$LoginName#academic.mydomain.k12.pa.us"
EmailAddress = "$LoginName#mydomain.org"
Company = "$LoginName#mydomain.org"
EmployeeID = $User.'Other ID'
HomeDirectory = "$FileServer\$LoginName"
HomeDrive = "Z:"
AccountPassword = $AccountPass
Enabled = $True
PasswordNeverExpires = $True
CannotChangePassword = $True
Path = 'OU={0},OU=Students,OU={1},OU=accounts,DC=academic,DC=mydomain,DC=k12,DC=pa,DC=us' -f #(
$User.'Grad Year',
$SchoolCodes[$User.School].Name)
Server = $ADServer
WhatIf = $False
} # End ADUserParams
Try {
# Create new user.
New-ADUser #ADUserParams -Verbose -ErrorAction Stop
}
Catch {
# If there's an error, write the error to the event log.
Write-EventLog -LogName $EventLogName -Source $EventLogSources[0][1] -EntryType Warning -EventId $EventLogSources[0][0] -Message "Something went wrong with the creation of a new user, [$LoginName] : $_"
}}}
If I run ADUserParams I can see the AccountPassword parameter is System.Security.SecureString so I figured that was a good thing. So, what am I doing wrong? I'd like to think either of those approaches would work - so long as I got everything correct. However, as I said, I cannot authenticate unless I revert to the old code which cannot handle accounts without a middle initial.
I think you have the order of things a bit wrong. You are creating $AccountPass using variables $FirstName and $LastName BEFORE you have defined them.
This should work
# CSV file being imported.
$CsvFile = "$env:USERPROFILE\Downloads\SampleData.csv"
# Import the contents of the CSV file.
$Users = Import-Csv -Path "$CsvFile"
# Loop through each line of the CSV, creating the user if the account doesn't already exist in AD.
ForEach ($User in $Users) {
[String]$LoginName = $User.'Stu Access Login'
If (-Not (Get-ADUser -Server $ADServer -Filter {SamAccountName -eq $LoginName})) {
$FirstName = $User.'Student First Name'
$LastName = $User.'Student Last Name'
# generate passwords
If (!([String]::IsNullOrEmpty($User.I))) {
# this person has an initial to use in the password
$AccountPass = ConvertTo-SecureString -String (
'{0}{1}{2}#{3}' -f #(
$FirstName[0].ToString().ToUpper(),
$User.I[0].ToString().ToLower(),
$LastName[0].ToString().ToLower(),
$User.'Other ID')) -AsPlainText -Force
}
Else {
# this person does not have an initial to work with
$AccountPass = ConvertTo-SecureString -String (
'{0}{1}#{2}' -f #(
$FirstName[0].ToString().ToUpper(),
$LastName[0].ToString().ToLower(),
$User.'Other ID')) -AsPlainText -Force
}
$ADUserParams = #{
Name = "$FirstName $LastName"
SamAccountName = $LoginName
GivenName = $FirstName
Initials = $User.'I'
Surname = $LastName
DisplayName = "$FirstName $($User.'I') $LastName"
Description= $SchoolCodes[$User.School].Name
ScriptPath= "student.bat"
UserPrincipalName = "$LoginName#academic.mydomain.k12.pa.us"
EmailAddress = "$LoginName#mydomain.org"
Company = "$LoginName#mydomain.org"
EmployeeID = $User.'Other ID'
HomeDirectory = "$FileServer\$LoginName"
HomeDrive = "Z:"
AccountPassword = $AccountPass
Enabled = $True
PasswordNeverExpires = $True
CannotChangePassword = $True
Path = 'OU={0},OU=Students,OU={1},OU=accounts,DC=academic,DC=mydomain,DC=k12,DC=pa,DC=us' -f #(
$User.'Grad Year',
$SchoolCodes[$User.School].Name)
Server = $ADServer
WhatIf = $False
} # End ADUserParams
Try {
# Create new user.
New-ADUser #ADUserParams -Verbose -ErrorAction Stop
}
Catch {
# If there's an error, write the error to the event log.
Write-EventLog -LogName $EventLogName -Source $EventLogSources[0][1] -EntryType Warning -EventId $EventLogSources[0][0] -Message "Something went wrong with the creation of a new user, [$LoginName] : $_"
}}}

Store credentials on button

To make it easier for new employees to connect to Exchange via powershell im writting a quick little tools using powershell:winforms.
The tool will just contain a couple of buttons, text field, en informative text so users can manage the Exchange envoirement from one UI.
#1 Set Credentials Button
$Set_Credential = New-Object System.Windows.Forms.Button
$Set_Credential.Top = "5"
$Set_Credential.Left = "5"
$Set_Credential.Anchor = "Left,Top"
$Set_Credential.Text = 'Set Credentials'
$Set_Credential.add_Click({
$UserCredential = Get-Credential
})
$Exchange_Manager.Controls.Add($Set_Credential)
#3 Connect to Exchange Online
$Exchange_Connect = New-Object System.Windows.Forms.Button
$Exchange_Connect.Top = "5"
$Exchange_Connect.Left = "150"
$Exchange_Connect.Anchor ="Left,Top"
$Exchange_Connect.Text = 'Connect'
$Exchange_Connect.add_Click({
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential "$UserCredential" -Authentication Basic -AllowRedirection
Import-PSSession $Session
})
$Exchange_Manager.Controls.Add($Exchange_Connect)
As you can see the first 'Set Credentials' button allows for users to enter credentials via the default get-credentials.
The Second button 'Connect to Exchange' starts a new PSSession using the $UserCredentials to login.
The issue i'm facing is that when pressing the connect button i get an error that says the -credential value is Null.
Querying the $UserCredential also gives an empty line.
It seems that altrough the credentials get entered they dont get stored because they are called trough an button function.
I tried looking it up but most of the anwsers are quite vague.
Ful code:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$Exchange_Manager = New-Object Windows.Forms.Form
#Session Initation 1
#1 Set Credentials Button
$Set_Credential = New-Object System.Windows.Forms.Button
$Set_Credential.Top = "5"
$Set_Credential.Left = "5"
$Set_Credential.Anchor = "Left,Top"
$Set_Credential.Text = 'Set Credentials'
$Set_Credential.add_Click({
$UserCredential = Get-Credential
})
$Exchange_Manager.Controls.Add($Set_Credential)
#2 Current Credentials
#3 Connect to Exchange Online
$Exchange_Connect = New-Object System.Windows.Forms.Button
$Exchange_Connect.Top = "5"
$Exchange_Connect.Left = "150"
$Exchange_Connect.Anchor ="Left,Top"
$Exchange_Connect.Text = 'Connect'
$Exchange_Connect.add_Click({
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential "$UserCredential" -Authentication Basic -AllowRedirection
Import-PSSession $Session
})
$Exchange_Manager.Controls.Add($Exchange_Connect)
#Information
#1 Set Credentials Button
$Mailboxes = New-Object System.Windows.Forms.Button
$Mailboxes.Top = "50"
$Mailboxes.Left = "5"
$Mailboxes.Anchor = "Left,Top"
$Mailboxes.Text = 'Get All Mailboxes'
$Mailboxes.add_Click({
Get-Mailbox |Select-Object DisplayName, Alias | Sort-Object -Property DisplayName | Out-gridview
})
$Exchange_Manager.Controls.Add($Mailboxes)
$Exchange_Manager.ShowDialog()
You need to make $UserCredential a global.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$Exchange_Manager = New-Object Windows.Forms.Form
$Exchange_Manager.TopMost = $true
# create a global empty credential object
$Global:UserCredential = [System.Management.Automation.PSCredential]::Empty
#Session Initation 1
#1 Set Credentials Button
$Set_Credential = New-Object System.Windows.Forms.Button
$Set_Credential.Top = "5"
$Set_Credential.Left = "5"
$Set_Credential.Anchor = "Left,Top"
$Set_Credential.Text = 'Set Credentials'
$Set_Credential.add_Click({
$Global:UserCredential = Get-Credential
})
$Exchange_Manager.Controls.Add($Set_Credential)
#2 Current Credentials
#3 Connect to Exchange Online
$Exchange_Connect = New-Object System.Windows.Forms.Button
$Exchange_Connect.Top = "5"
$Exchange_Connect.Left = "150"
$Exchange_Connect.Anchor ="Left,Top"
$Exchange_Connect.Text = 'Connect'
$Exchange_Connect.add_Click({
if ($Global:UserCredential -ne [System.Management.Automation.PSCredential]::Empty) {
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential "$Global:UserCredential" -Authentication Basic -AllowRedirection
Import-PSSession $Session
}
})
$Exchange_Manager.Controls.Add($Exchange_Connect)
#4 Show the dialog and dispose of it afterwards
$Exchange_Manager.ShowDialog()
$Exchange_Manager.Dispose()

Create a save button in winforms

Goal:
I am attempting to create a button that would save a file to desktop. The incoming file is fetched with an Invoke-WebRequest using the GET method. I want the save button to be in my pop-up window.
Here is an example:
Side note:
This code is sitting in a switch with a variable split three ways.
switch (...) {
p {
if ($second -match 'RegexMatch') {
$resource = $second
$fileResult = Invoke-WebRequest -Uri https://url.com/$resource/file -WebSession $currentsession
# End API Call
Write-Host
Write-Host '------------' -ForegroundColor Green
Write-Host 'FILE Results' -ForegroundColor Green
Write-Host '------------' -ForegroundColor Green
# Create Window
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object -TypeName System.Windows.Forms.Form
$form.StartPosition = 'CenterScreen'
$form.KeyPreview = $true
$form.Add_KeyDown {
if ($_.Control -and $_.KeyCode -eq 'F') {
Add-Type -AssemblyName Microsoft.VisualBasic
$stringToFind = [Microsoft.VisualBasic.Interaction]::InputBox('Please enter your search terms', 'Find')
$pos = $textBox.Text.IndexOf($stringToFind)
if ($pos -ne -1) {
$textBox.SelectionStart = $pos
$textBox.SelectionLength = $stringToFind.Length
}
}
}
# Textbox
$textBox = New-Object -TypeName System.Windows.Forms.TextBox
$textBox.Dock = [Windows.Forms.DockStyle]::Fill
$textBox.ReadOnly =$true
$textBox.Multiline = $true
$textBox.ScrollBars = 'Vertical'
$textBox.Font = New-Object -TypeName System.Drawing.Font -ArgumentList ('Arial',12)
$textBox.ForeColor = 'White'
$textBox.Text = $fileResult
$textBox.BackColor = 'Black'
$textBox.ShortcutsEnabled = $true
$Form.Controls.Add($textBox)
# Button
$btn = New-Object -TypeName System.Windows.Forms.Button
$btn.Text = 'Finish'
$btn.DialogResult = 'Ok'
$btn.Dock = 'bottom'
$form.Controls.Add($btn)
if ($form.ShowDialog() -eq 'Ok') {
$tb.lines
}
} else {
Write-Host
Write-Warning -Message 'Please enter a valid FILE ID'
Write-Host
}
break
}
...
}
Purpose:
I want to add an option for the user to download the file for a closer look in a different application.
Question:
How would I begin to create a button utilizing winforms in powershell to save this file to disk?
Here is what I have tried:
$BtnSave=New-Object -TypeName System.Windows.Forms.Button
$BtnSave.Text='Save'
$BtnSave.Dock='bottom'
$btnSave.DialogResult='Ok'
$form.Controls.Add($BtnSave)
$BtnSave.Add_Click({
$SaveFileDialog = New-Object 'System.Windows.Forms.SaveFileDialog'
if ($SaveFileDialog.ShowDialog() -eq 'Ok')
{
$textBox.Text = $SaveFileDialog.FileName
Write-Information 'File Saved'
}
})
New Problem:
File is not saving to disk still, but the save file dialog does show up on click. In addition, using Switch -OutFile with my Invoke-WebRequest is shooting me an error.
Error:
Invoke-WebRequest : Missing an argument for parameter 'OutFile'. Specify a parameter of type 'System.String' and try again.
After adding a button where you want in the form, the Add_Click() method will allow you to handle its click event and run any scriptblock you want (when button is clicked).
At this point, the -OutFile argument for Invoke-WebRequest will help with saving the downloaded file to disk (pass it the desired path to the file).

Powershell filedownload from https

I am trying to download several files using Powershell and its Invoke-WebRequest method.
I'm basically looping through several filenames (I know that they are available on the server) and download them.
My problem is that my script works for the first file and fails for every file that follows. When I open one of the later files (.csv`s) there is just some html code in it).
I already read a lot about passing session cookings but I am not sure if this is my problem or how I can do that.
My script so far looks like this:
$httpsUser = 'XXX'
$httpsPass = 'YYY'
foreach ($instrument in 'ivv','ijh','ijr','iwm') {
$Source = 'https://***', `
$instrument, '-en_us.csv' -join ""
$Target = 'C:\User\', `
$instrument, '-en_us.csv' -join ""
$uri = New-Object “System.Uri” “$Source”
$WebClient = [System.Net.HttpWebRequest]::Create($uri)
$webclient.Proxy.Credentials =
[System.Net.CredentialCache]::DefaultNetworkCredentials
$webclient.Credentials =
New-Object System.Net.NetworkCredential($httpsUser,$httpsPass)
Invoke-WebRequest -Uri $Source -OutFile $Target
}
Thank you all and let me know what you think :)
It seems like you don't using the HttpWebRequest you created to download the file. Anyway, I would recommend using System.Net.WebClient:
$wc = New-Object System.Net.WebClient
$wc.Credentials = New-Object System.Net.NetworkCredential($httpsUser,$httpsPass)
$wc.DownloadFile($Source, $target)
Try using webclient methods DownloadFile(src,dst). Should be something like this:
$httpsUser = 'XXX'
$httpsPass = 'YYY'
foreach ($instrument in 'ivv','ijh','ijr','iwm') {
$Source = 'https://***', `
$instrument, '-en_us.csv' -join ""
$Target = 'C:\User\', `
$instrument, '-en_us.csv' -join ""
$webclient = New-Object -TypeName Net.WebClient
$webclient.Encoding = [System.Text.Encoding]::UTF8
$webclient.UseDefaultCredentials = $true
$webclient.Proxy.Credentials = New-Object System.Net.NetworkCredential($httpsUser,$httpsPass)
$webclient.DownloadFile($Source,$Target)
}
Thank you all for your answers. I figured out now that the site generates a security token while logging in. This token needs to be passed in every webrequest. I was not yet able to figure out how to do that with powershell but know that perl has a build in function (called $merch) for exactly this problem.
To solve my problem I had to automatize the IE :( (I know this is not the most sophisticated way but right now it is the quickest solution. If anyone is interested here is the code for that:
$ie = new-object -ComObject 'InternetExplorer.Application'
$requestUri = 'https://www.trololo.com'
$userIdFragment = "userName";
$passwordIdFragment = "password";
$buttonIdFragment = "submitLogin";
$ie.visible = 'false'
$ie.navigate($requestUri)
while($ie.Busy) { Start-Sleep -Milliseconds 100 }
$doc1 = $ie.Document
$doc1.getElementsByTagName("input") | % {
if ($_.id -ne $null){
if ($_.id.Contains($buttonIdFragment)) { $btn = $_ }
if ($_.id.Contains($passwordIdFragment)) { $pwd = $_ }
if ($_.id.Contains($userIdFragment)) { $user = $_ }
}
}
$user.value = "XXXX"
$pwd.value = "YYYY
$btn.disabled = $false
$btn.click()
while($ie.Busy) { Start-Sleep -Milliseconds 5000 }
$ie.navigate($requestUri)
while($ie.Busy) { Start-Sleep -Milliseconds 200 }
$doc1 = $ie.Document
$link = $doc1.getElementsByTagName("a") | where-object {$_.href -match "Your String"}
$link.click()
Start-Sleep -Milliseconds 1000
$wshell = new-object -com wscript.shell
$wshell.appactivate("Internet Explorer")
$wshell.sendkeys("%s")

Powershell - How to include array data with flat file

I am creating a csv flat with some data. One of the column is content type which may have one or more content types in the column (string values). How do I include all the values from the array object into the flat file but with the respected row? See the code below... I passed $ct to the add-content but flat file does not have the content type data in it. when I do write-host for $ct I see that it has values. Please suggest.
#For Output file generation
$OutputFN = "Libraries.csv"
#delete the file, If already exist!
if (Test-Path $OutputFN)
{
Remove-Item $OutputFN
}
#Write the CSV Headers
Add-Content $OutputFN "Web URL, Site Name, Library Name, Content Types"
$systemlibs =#("Converted Forms", "Customized Reports", "Documents", "Form Templates",
"Images", "List Template Gallery", "Master Page Gallery", "Pages",
"Reporting Templates", "Site Assets", "Site Collection Documents",
"Site Collection Images", "Site Pages", "Solution Gallery",
"Style Library", "Theme Gallery", "Web Part Gallery", "wfpub")
#Get the Site collection
$Site= Get-SPSite "http://inside.contoso.com"
$spWebApp = $Site.WebApplication
foreach($allSites in $spWebApp.Sites)
{
#Loop through all Sub Sites
foreach($Web in $allSites.AllWebs)
{
#Write-Host "-----------------------------------------------------"
#Write-Host "Site Name: '$($web.Title)' at $($web.URL)"
#Write-Host "-----------------------------------------------------"
foreach($list in $Web.Lists)
{
if($list.BaseTemplate -eq "DocumentLibrary" -and $list.AllowContentTypes -eq $true)
{
if(-not ($systemlibs -Contains $list.Title))
{
if ($list.AllowContentTypes -eq $true)
{
$ct = #()
foreach ($contenttype in $list.ContentTypes)
{
$ctProperties = #{ContentType = $contenttype.Name}
$ctObject = New-Object PSObject -Property $ctProperties
#write-host $ct
$ct += $ctObject
#write-host $ct
#Write-Host "$($web.URL), $($web.Title), $($list.Title), $ct"
}
#$ct
write-host $ct
#Write-Host "$($web.URL), $($web.Title), $($list.Title), $($ct)"
$content = $web.URL + "," + $web.Title +"," + $list.Title +"," + $ct
add-content $OutputFN $content
}
}
}
}
}
}
If you wanted to include data in a csv file that could be later converted to an array you could just store it as a delimited string. The delimiter obviously cannot be the same as the one you are using in your flat file. In its basic form you could be using -split and -join
$array = #("a","b","c")
Add-Content $path ($array -join ";")
In context we could make a couple of changes for the better. In place of this if statement
if ($list.AllowContentTypes -eq $true){
# Stuff happens here
}
You could instead do the following.
if ($list.AllowContentTypes -eq $true){
$ct = $list.ContentTypes | Select-Object -ExpandProperty Name
$content = "$($web.URL), $($web.Title), $($list.Title), $($ct -join ";")"
add-content $OutputFN $content
}
You could take this farther using Objects and ConvertTo-CSV but lets be sure we are on the right track first.

Resources