PowerShell New-Object and HTML Output Issue - html

I am trying to create statistics for a server using PowerShell. and also learning PowerShell.
This is the code I have
Function Get-PingHost
{
[cmdletbinding()]
Param([string]$Hostname = "ServerName")
"The HostName is " + $Hostname
$status=get-wmiobject win32_pingstatus -Filter "Address='$Hostname'" | Select-Object statuscode
"The Status Code is " + $status.statuscode
if($status.statuscode -eq 0)
{
$HostNameStatusInfo = $Hostname + " is REACHABLE"
}
else
{
$HostNameStatusInfo = $Hostname + " is NOT REACHABLE"
}
New-Object -TypeName PSObject -Property $HostNameStatusInfo
**<COMMENT>** If I remove the above New-Object code is still get an incorrect output as mentioned below in the **HTML OutPut**
}
$fragments = #()
$fragments+=$top
$fragments+="<a href='javascript:toggleAll();' title='Click to toggle all sections'>Expand All/Collapse All</a>"
$Text = "SQL Server Ping Status"
$div = $Text.Replace(" ","_")
$fragments+= "<a href='javascript:toggleDiv(""$div"");' title='click to collapse or expand this section'><h2>$Text</h2></a><div id=""$div"">"
$fragments+= Get-PingHost -Hostname $Hostname | ConvertTo-Html -Fragment -As List
$fragments+="</div>"
$fragments+= $html.InnerXml
$fragments+="</div>"
$fragments+= "<p class='footer'>$(get-date)</p>"
The Output I get in HTML is:
SQL Server Ping Status
*: 31
*: 20
Error Message
ew-Object : Cannot bind parameter 'Property'. Cannot convert the "ServerName is REACHABLE" value of type "System.String" to type "System.Collections.IDictionary".
At line:38 char:45
+ New-Object -TypeName PSObject -Property $HostNameStatusInfo
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-Object], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.NewObjectCommand

Apologies... The issue has been resolved...
The code is as below: -
#GET OPERATING SYSTEM INFORMATION
Function Get-PingHost
{
[cmdletbinding()]
Param([string]$Hostname = "ServerName")
$status=get-wmiobject win32_pingstatus -Filter "Address='$Hostname'" | Select-Object statuscode
#"The Status Code is " + $status.statuscode
if($status.statuscode -eq 0)
{
$Hostname + " is <b>REACHABLE</b>"
}
else
{
$Hostname + " is <b>NOT REACHABLE</b>"
#Basically You Don't Need to assign this to the String Variable and
#then display the string variable...
}
}
#endregion
$fragments = #()
$top = ""
$fragments+=$top
$fragments+="<a href='javascript:toggleAll();' title='Click to toggle all sections'>Expand All/Collapse All</a>"
$Text = "SQL Server Ping Status"
$div = $Text.Replace(" ","_")
$fragments+= "<a href='javascript:toggleDiv(""$div"");' title='click to collapse or expand this section'><h2>$Text</h2></a><div id=""$div"">"
$fragments+= Get-PingHost -Hostname $Hostname
$fragments+="</div>"
$fragments+= $html.InnerXml
$fragments+="</div>"
$fragments+= "<p class='footer'>$(get-date)</p>"

Related

"The property cannot be found on this object. Verify that the property exists." Property definitely exists

I can't figure out why I am not able to change these variables. If I type them into the console in debug mode, then it prints the values. It is also strange how I am able to change the allowedRequestors variable on line 24, but not any of the others. Does anyone know why this is happening to the other variables?
$FilePath = "C:\Users\Desktop\TestScripts\testBulkAP.csv"
$headers = & $PSScriptRoot\GetToken.ps1
## preparing create Catalog data
$accesspacakgeRequest = '{"displayName":"","description":"sddsds","isHidden":false,"catalogId":"","accessPackageResourceRoleScopes":[],"accessPackageAssignmentPolicies":[{"displayName":"Initial Policy","description":"Initial Policy","durationInDays":365,"expirationDateTime":null,"canExtend":false,"requestApprovalSettings":null,"accessReviewSettings":null,"notificationSettings":null,"additionalInfo":null,"isDenyPolicy":false,"id":"","activeAssignmentCount":0,"accessPackageId":"00000000-0000-0000-0000-000000000000","accessPackageCatalog":null,"createdDateTime":null,"modifiedDateTime":null,"createdBy":"","modifiedBy":"","countOfUsersIncludedInPolicy":null,"requestorSettings":{"acceptRequests":true,"scopeType":"NoSubjects","allowedRequestors":[],"isOnBehalfAllowed":false},"questions":[]}]}'
$emlRequestUrl = "https://graph.microsoft.com/beta/identityGovernance/entitlementManagement/accessPackages"
$accesspacakgeRequestObject = ConvertFrom-Json -InputObject $accesspacakgeRequest
$Content = Import-Csv $FilePath
foreach($assignmentData in $Content) {
$accesspacakgeRequestObject.catalogId = $assignmentData.catalogId
$accesspacakgeRequestObject.displayName = $assignmentData.displayName
$accesspacakgeRequestObject.description = $assignmentData.description
$accesspacakgeRequestObject.accessPackageAssignmentPolicies.requestorSettings.scopeType = $assignmentData.scope
if ($assignmentData.scope -eq "SpecificDirectorySubjects") {
$accesspacakgeRequestObject.accessPackageAssignmentPolicies.requestorSettings.allowedRequestors += New-Object -TypeName psobject -Property #{'#odata.type' = '#microsoft.graph.groupMembers'; 'id' = $assignmentData.groupId; 'description' = $assignmentData.groupName; 'isBackup' = 'false'}
}
$numApprovalStages = [int]$assignmentData.approvalStages
if ($numApprovalStages -gt 0) {
$accesspacakgeRequestObject.accessPackageAssignmentPolicies.requestApprovalSettings += New-Object -TypeName psobject -Property #{'approvalMode' = 'Serial'; 'isApprovalRequired' = 'true'; 'isApprovalRequiredForExtension' = 'false'; 'isRequestorJustificationRequired' = 'false'; 'approvalStages' = #()}
for ($i=1;$i -le [int]$assignmentData.approvalStages; $i++)
{
$accesspacakgeRequestObject.accessPackageAssignmentPolicies.requestApprovalSettings.approvalStages += New-Object -TypeName psobject -Property #{'approvalStageTimeOutInDays' = '14'; 'primaryApprovers' = #(); escalationApprovers = #();'isEscalationEnabled' = 'false'; 'escalationTimeInMinutes' = '0'; 'isApproverJustificationRequired' = 'true'}
$accesspacakgeRequestObject.accessPackageAssignmentPolicies.requestApprovalSettings.approvalStages.primaryApprovers += New-Object -TypeName psobject -Property #{'#odata.type' = '#microsoft.graph.singleUser'; "displayName" = ''; 'objectId' = Get-Variable -Name "assignmentData.approver$1" -ValueOnly; 'isBackup' = 'false'}
}
}
$requestbody = $accesspacakgeRequestObject | ConvertTo-Json -Depth 10
$response = Invoke-RestMethod $emlRequestUrl -Headers $headers -Method Post -Body $requestbody -UseBasicParsing -ErrorAction Continue
}
The error message is:
The property 'requestApprovalSettings' cannot be found on this object. Verify that the property exists and can be set.
At C:\Users\Desktop\TestScripts\AddAccessPackageAndPolicyWITHAPPROVER.ps1:30 char:5
+ $accesspacakgeRequestObject.accessPackageAssignmentPolicies.reque ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException
The property 'approvalStages' cannot be found on this object. Verify that the property exists and can be set.
At C:\Users\Desktop\TestScripts\AddAccessPackageAndPolicyWITHAPPROVER.ps1:34 char:9
+ $accesspacakgeRequestObject.accessPackageAssignmentPolicies.r ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
Get-Variable : Cannot find a variable with the name 'assignmentData.approver1'.
At C:\Users\Desktop\TestScripts\AddAccessPackageAndPolicyWITHAPPROVER.ps1:35 char:250
+ ... objectId' = Get-Variable -Name "assignmentData.approver$i" -ValueOnly ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (assignmentData.approver1:String) [Get-Variable], ItemNotFoundException
+ FullyQualifiedErrorId : VariableNotFound,Microsoft.PowerShell.Commands.GetVariableCommand
The property 'primaryApprovers' cannot be found on this object. Verify that the property exists and can be set.
At C:\Users\Desktop\TestScripts\AddAccessPackageAndPolicyWITHAPPROVER.ps1:35 char:250
+ ... objectId' = Get-Variable -Name "assignmentData.approver$i" -ValueOnly ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
The .accessPackageAssignmentPolicies property contains an array ([...]-enclosed in the JSON input).
Even though that array happens to contain only one element, you still need to access it by index in order to set its (only) element's properties; e.g.:
# Note the `[0]`
$accesspacakgeRequestObject.accessPackageAssignmentPolicies[0].requestorSettings.scopeType = $assignmentData.scope
Note that getting properties does not strictly require this index access, because of a feature called member-access enumeration.
This perhaps surprising asymmetry - requiring indexed access on setting - is by design, however - see this answer for more information.

How to call a function within a foreach parallel loop

Good evening.
I'm trying to use parallelism for the first time but I don't understand how to call a function within foreach loop.
I get a series of this error: Cannot bind argument to parameter 'Path' because it is null.
This is what I've done so far:
$FolderPath = "C:\myfolder\"
function AppendLog ($client) {
$so = New-CimSessionOption -Protocol 'DCOM'
$s = New-CimSession -ComputerName $client -SessionOption $so
Add-Content -Path (join-path $folderpath "LOGS.txt") -Value ( (get-date -Format "[yyyy.MM.dd HH:mm:ss]").tostring() + $client + " -PING: OK")
$arch = Get-CimInstance –query "select * from win32_operatingsystem" -CimSession $s | select -expandproperty osarchitecture
Add-Content -Path (join-path $folderpath "LOGS.txt") -Value ( (get-date -Format "[yyyy.MM.dd HH:mm:ss]").tostring() + $client + " -ARCH:" + $arch )
$lastboot = Get-CimInstance –query "select * from win32_operatingsystem" -CimSession $s | select -expandproperty lastbootuptime
Add-Content -Path (join-path $folderpath "LOGS.txt") -Value ( (get-date -Format "[yyyy.MM.dd HH:mm:ss]").tostring() + $client + " -BOOT:" + $lastboot )
}
$funcDef = $function:AppendLog.ToString()
$clients = get-content -path (join-path $folderPath "client.txt")
$clients | ForEach-Object -parallel {
if (test-connection $_ -count 2 -Quiet)
{
$function:AppendLog = $using:funcDef
AppendLog ($_)
}
} -throttlelimit 3
Could you explain me how to pass my path?
My bad on the comment, the error you're getting is most likely coming from your function. The error is being thrown by Join-Path:
PS /> Join-Path $null 'Logs.txt'
Join-Path : Cannot bind argument to parameter 'Path' because it is null.
At line:1 char:11
+ Join-Path $null 'Logs.txt'
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Join-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.JoinPathCommand
The reason is because $FolderPath doesn't exist in the scope of your parallel loop. $folderpath should be replace with $using:folderpath inside your function.
As a side note, adding information to the same file on a parallel execution doesn't seem to be a good idea.
Last edit, I understand if this is meant to test how ForEach-Object -Parallel works but again, if the cmdlet allows remote querying / remote execution with multiple hosts at the same time, let the cmdlet handle that for you, it is more efficient.
As for the code, this is what I would use with what you already have:
$FolderPath = "C:\myfolder\"
$sessionOption = New-CimSessionOption -Protocol 'DCOM'
$clients = Get-Content -Path (Join-Path $FolderPath -ChildPath "Client.txt")
$results = $clients | ForEach-Object -Parallel {
$out = #{
Time = [datetime]::Now.ToString('[yyyy.MM.dd HH:mm:ss]')
ComputerName = $_
}
if ($ping = Test-Connection $_ -Count 2 -Quiet)
{
$session = New-CimSession -ComputerName $_ -SessionOption $using:sessionOption
$OSInfo = Get-CimInstance -CimSession $session -ClassName win32_operatingSystem
Remove-CimSession $session
}
$out.Ping = $ping
$out.Arch = $OSInfo.OSArchitecture
$out.LastBoot = $OSInfo.LastBootUpTime
[pscustomobject]$out
} -ThrottleLimit 3
$results | Export-Csv "$myFolder\LOGS.csv" -NoTypeInformation
This will output an object like this below:
Time ComputerName Ping OSArchitecture LastBoot
---- ------------ ---- -------------- --------
[2021.06.19 20:06:00] ComputerName01 True 64-bit 6/16/2021 11:47:16 AM
[2021.06.19 20:07:00] ComputerName02 False
[2021.06.19 20:08:00] ComputerName03 True 64-bit 6/13/2021 11:47:16 AM
[2021.06.19 20:09:00] ComputerName04 True 64-bit 6/14/2021 11:47:16 AM
[2021.06.19 20:10:00] ComputerName05 True 64-bit 6/15/2021 11:47:16 AM
Which can be exported nicely to a CSV instead of a text file. P.D.: sorry for the syntax highlighting :(

Powershell using "at-run" parameters to call functions based on which parameter is called

How to use the "at-run" parameters as parameters within the function.
Right now my code is returning all data/value regardless of what is enter for the "at-run" parameters.
For example, I will like to match the $Server_Name from the "at-run" parameters with the foreach loop, so that only data with the matching $Server_Name is return.
function StartServer{
Param (
[Parameter (Mandatory=$true)] [STRING] $Region,
[Parameter (Mandatory=$true)] [STRING] $Env,
#[STRING] $Env,
[Parameter (Mandatory=$true)] [STRING] $ScriptName, #$ScriptName
[Parameter (Mandatory=$true)] [STRING] $Server_Name
#[STRING] $Server,
#[STRING] $Services
)
#Write-Output ("Region: "+ $Region + " Env: " + $Env + " ScriptName: " + $ScriptName + " Server_Name: " + $Server)
#$startDate = Get-Date;
$Sourcefile = "C:\Users\ Scripts\CLEAN10182019.csv"
$StartProperties = (Import-Csv $Sourcefile)
$Sorted = $StartProperties |select Server_Name,Service_Name, Start_Order, Start_Flag, Start_Wait | Sort-Object -Property Server_Name, {[int]$_.Start_Order}|Format-Table
#$Sorted
foreach($line in $StartProperties)
{
$Server_Name = [string]$line.'Server_Name'
$Service_Name = [string]$line.'Service_Name'
$Start_Order = [string]$line.'Start_Order'
$Start_Wait= [string]$line.'Start_Wait'
$file = #"Matches? " + $Server_Name
"Start " + $Service_Name + " " + " Timeout " + $Start_Wait
Write-Output $file
}
}
StartServer
The quickest and easiest way I can think of might be to only execute the code if the service name is passed in-
foreach($line in $StartProperties) {
if ([string]$line.'Server_Name' -eq $Server_Name) {
$Server_Name = [string]$line.'Server_Name'
$Service_Name = [string]$line.'Service_Name'
$Start_Order = [string]$line.'Start_Order'
$Start_Wait= [string]$line.'Start_Wait'
$file = #"Matches? " + $Server_Name
"Start " + $Service_Name + " " + " Timeout " + $Start_Wait
Write-Output $file
}
}
Does that look like what you're looking for?
If I understand you correctly, you want match only those CSV rows whose Server_Name column matches the $Server_Name parameter value:
Import-Csv $Sourcefile | Where-Object Server_Name -eq $Server_Name | ForEach-Object {
# Synthesize and output a filename.
"Start " + $_.Service_Name + " " + " Timeout " + $_.Start_Wait
}
If you want to output the synthesized string as part of an object, to be presented in tabular form on output, do something like the following:
Import-Csv $Sourcefile | Where-Object Server_Name -eq $Server_Name | ForEach-Object {
# Create a custom object with the properties of interest:
[pscustomobject] #{
# Other properties
Server_Name = $_.Server_Name # ...
FileName = "Start " + $_.Service_Name + " " + " Timeout " + $_.Start_Wait
}
}
If your output objects have 4 or fewer properties, they automatically print as if they had been piped to Format-Table, so you'll get something like the following:
Server_Name FileName
----------- --------
server1 Start service1 Timeout 20
server2 Start service2 Timeout 10
...

How to change cell color in HTML table in a PowerShell script using if condition

I have this below script to calculate the age of snapshots (snapshots older than one day to be precise) in our environment. My code and current output are as below:
Add-PSsnapin VMware.VimAutomation.Core
# HTML formatting
$a = "<style>"
$a = $a + "BODY{background-color:white;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: LightBlue}"
$a = $a + "TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: white}"
$a = $a + "</style>"
Connect-VIServer -Server ******-User *****-Password *****
# Main section of check
Write-Host "Checking VMs for for snapshots"
$date = Get-Date
$datefile = Get-Date -UFormat '%m-%d-%Y-%H%M%S'
$filename = "C:\Temp\snaps_older_than_3\" + $datefile + ".htm"
$ss = Get-VM |
Get-Snapshot |
Where {$_.Created -lt (Get-Date).AddDays(-1)} |
Select-Object vm, name, SizeGB, SizeMB, Created, powerstate,
#{Name='Age';Expression={(New-TimeSpan -Start $_.Created -End $date).Days}} |
ConvertTo-Html -Head $a -Body "<H2>VM Snapshot Report </H2>" |
Out-File $filename
Write-Host " Complete " -ForegroundColor Green
Write-Host "Your snapshot report has been saved to:" $filename
$SMTPServer = "******"
$SMTPPort = 25
$username = "******"
#Define the receiver of the report
$to = "******"
$subject = "VM Snapshot Report"
$body = "VM Snapshot Report"
$attachment = New-Object Net.Mail.Attachment($filename)
$message = New-Object System.Net.Mail.MailMessage
$message.Subject = $subject
$message.Body = $body
$message.To.Add($to)
$message.From = $username
$message.Attachments.Add($attachment)
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $false
#$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.Send($message)
Write-Host "Mail Sent"
What I want to do is, in the below piece of code I want to determine the color of my age cell according to the age value. For example, if age is less than 3 I want that cell to be colored in green, if it is 3 should be in blue, if it crosses it should be red.
$ss = Get-VM |
Get-Snapshot |
Where {$_.Created -lt (Get-Date).AddDays(-1)} |
Select-Object vm, name, SizeGB, SizeMB, Created, powerstate,
#{Name='Age';Expression={(New-TimeSpan -Start $_.Created -End $date).Days}} |
ConvertTo-Html -Head $a -Body "<H2>VM Snapshot Report </H2>" |
Out-File $filename
I have googled a lot and found below piece of code by someone, but I seriously cannot make this work.
$servers = "mccoyb02", "foo", "mccoyb01", "reisrv1"
$path = "c:\ephemeral\uptime.html"
$header = #"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>System Status Report</title>
<style type="text/css">
<!--
body {
background-color: #E0E0E0;
font-family: sans-serif
}
table, th, td {
background-color: white;
border-collapse:collapse;
border: 1px solid black;
padding: 5px
}
-->
</style>
"#
$body = #"
<h1>Server Status</h1>
<p>The following report was run on $(get-date).</p>
"#
$results = foreach ($server in $servers) {
if (Test-Connection $server -Count 1 -EA 0 -Quiet) {
$status = "Up"
} else {
$status = "Down"
}
[PSCustomObject]#{
ServerName = $server
Reuslts = $status
}
}
$results | ConvertTo-Html -Head $header -Body $body | foreach {
$PSItem -replace "<td>Down</td>", "<td style='background-color:#FF8080'>Down</td>"
} | Out-File C:\Ephemeral\uptime.html
& .\uptime.html
Output:
the video from Jeffrey Hicks (https://www.youtube.com/watch?v=QdK3qM5jnYw) pointed me in the right direction. And the article at Petri got the solution to style cells (and not only rows). I recommend you also watch/read those.
I've edited also the $head variable to a Here-String for easier reading and editing.
The styling of cells requires it to first convert the object to a XML object (mind the -Fragment switch!). This XML object can then be parsed, edited and finally converted to HTML. Hope the comments are clear.
Add-PSsnapin VMware.VimAutomation.Core
# HTML formatting
$head = #"
<style>
BODY{background-color:white;}
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: LightBlue}
TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: white}
.green{background-color:#d5f2d5}
.blue{background-color:#e0eaf1}
.red{background-color:#ffd7de}
</style>
"#
Connect-VIServer -Server ****** -User ***** -Password *****
# Main section of check
Write-Host "Checking VMs for for snapshots"
$date = Get-Date
$datefile = Get-Date -UFormat '%m-%d-%Y-%H%M%S'
$filename = "C:\Temp\snaps_older_than_3\" + $datefile + ".htm"
# Convert the selection to an XML object
[xml]$xmlObject = Get-VM |
Get-Snapshot |
Where {$_.Created -lt (Get-Date).AddDays(-1)} |
Select-Object vm, name, SizeGB, SizeMB, Created, powerstate,
#{Name='Age';Expression={(New-TimeSpan -Start $_.Created -End $date).Days}} |
ConvertTo-Html -Fragment
# Parse XML object and set colour class according to value in last column ("Age")
for($i=1;$i -le $xmlObject.table.tr.count-1;$i++) {
if(($xmlObject.table.tr[$i].td[-1] -as [int]) -lt 3) {
$xmlObject.table.tr[$i].ChildNodes[($xmlObject.table.tr[$i].ChildNodes.Count-1)].SetAttribute('class','green')
} elseif (($xmlObject.table.tr[$i].td[-1] -as [int]) -eq 3) {
$xmlObject.table.tr[$i].ChildNodes[($xmlObject.table.tr[$i].ChildNodes.Count-1)].SetAttribute('class','blue')
} else {
$xmlObject.table.tr[$i].ChildNodes[($xmlObject.table.tr[$i].ChildNodes.Count-1)].SetAttribute('class','red')
}
}
# Define body and append the modified XML object
$body = #"
<H2>VM Snapshot Report </H2>
$($xmlObject.innerxml)
"#
# Convert to HTML and save the file
ConvertTo-Html -Head $head -Body $body |
Out-File $filename
Write-Host " Complete " -ForegroundColor Green
Write-Host "Your snapshot report has been saved to:" $filename
$SMTPServer = "******"
$SMTPPort = 25
$username = "******"
#Define the receiver of the report
$to = "******"
$subject = "VM Snapshot Report"
$body = "VM Snapshot Report"
$attachment = New-Object Net.Mail.Attachment($filename)
$message = New-Object System.Net.Mail.MailMessage
$message.Subject = $subject
$message.Body = $body
$message.To.Add($to)
$message.From = $username
$message.Attachments.Add($attachment)
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $false
#$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.Send($message)
Write-Host "Mail Sent"

New-ADUser is not working properly when bulk loading via .csv file

I'm running into an error where this is not picking up the password field at all... I ran the import command manually to make sure it was grabbing all of the correct data, but it errors out on the password and group info...
$Users = Import-Csv -Path "C:\NewUsers.csv"
foreach ($User in $Users)
{
$Displayname = $User.'Firstname' + " " + $User.'Lastname'
$UserFirstname = $User.'Firstname'
$UserLastname = $User.'Lastname'
$OU = $User.'OU'
$SAM = $User.'SAM'
$UPN = $User.'Firstname' + "." + $User.'Lastname' + "#" + $User.'Maildomain'
$Password = $User.'Password'
$Description = $User.'Description'
$Group = $User.'Group'
$Account = New-ADUser -Name "$Displayname" -DisplayName "$Displayname" -SamAccountName $SAM -UserPrincipalName $UPN -GivenName "$UserFirstname" -Surname "$UserLastname" -Description "$Description" -AccountPassword (ConvertTo-SecureString $Password -AsPlainText -Force) -Enabled $true -Path "$OU" -ChangePasswordAtLogon $false -PasswordNeverExpires $true -server esg.intl -PassThru
Add-ADGroupMember -Identity $Group -Members $Account
}
And here are the errors I'm getting even though I know the passwords are ok:
New-ADUser : The password does not meet the length, complexity, or history requirement of the domain.
At C:\Users\A-Shane.Johnson\Desktop\Bulk Add Domain Users.ps1:24 char:13
+ ... $Account = New-ADUser -Name "$Displayname" -DisplayName "$Displaynam ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (CN=ESGAP PMOInt...,DC=esg,DC=intl:String) [New-ADUser], ADPasswordComplexi
tyException
+ FullyQualifiedErrorId : ActiveDirectoryServer:1325,Microsoft.ActiveDirectory.Management.Commands.NewADUser
Add-ADGroupMember : Cannot validate argument on parameter 'Members'. The argument is null or empty. Provide an
argument that is not null or empty, and then try the command again.
At C:\Users\A-Shane.Johnson\Desktop\Bulk Add Domain Users.ps1:26 char:46
+ Add-ADGroupMember -Identity $Group -Members $Account
+ ~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Add-ADGroupMember], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.AddADGrou
pMember
Depending on how that CSV is generated you might try double checking that there isn't an extra space in the Password column header? i.e. 'Password '. Sometimes that trips me up because it doesn't show in the excel or cmd line view easily.
I found the problem and answer. Apparently our domain has an issue when part of the username is in the password. I misunderstood that error thinking that the conversion is what was causing the issue...
I fixed the passwords and now the script runs beautifully!