Working on a script to get the status of Windows Defender components. Running into a challenge looping through the results from Get-MpComputerStatus as I'm wanting to write out which components are enabled and disabled.
The issue that I'm running into is I'm not able to get the specific value from the PSOBJECT and only get everything.
#Microsoft Defender Statuses
$DefenderStatus = Get-MpComputerStatus | select-object AMServiceEnabled, AntispywareEnabled, AntivirusEnabled, BehaviorMonitorEnabled, IoavProtectionEnabled, IsTamperProtected, IsVirtualMachine, NISEnabled, OnAccessProtectionEnabled, RealTimeProtectionEnabled
#Write results to a PSOBJECT
$result = ([ordered]#{
'Anti-Virus'=$DefenderStatus.AntivirusEnabled
'Anti-Malware'=$DefenderStatus.AMServiceEnabled
'Anti-Spyware'=$DefenderStatus.AntispywareEnabled
'Behavior Monitor'=$DefenderStatus.BehaviorMonitorEnabled
'Office-Anti-Virus'=$DefenderStatus.IoavProtectionEnabled
'NIS'=$DefenderStatus.NISEnabled
'Access Protection'=$DefenderStatus.OnAccessProtectionEnabled
'R-T Protection'=$DefenderStatus.RealTimeProtectionEnabled
})
foreach ($Status in $result)
{
If ($Status.Values -eq $false)
{
Write-Host "$Status.Keys is Disabled"
$Disabled = $Disabled + ", " + $Status
}
Else
{
Write-Host "$Status.Keys is Enabled"
$Enabled = $Enabled + ", " + $Status
}
}
As Theo comments, you're storing the results in an ordered hash table, if you want to iterate over the Key-Value pairs you could use .GetEnumerator() or .Keys property or .get_Keys() method, for example:
$result = [ordered]#{
'Anti-Virus' = $true
'Anti-Malware' = $true
'Anti-Spyware' = $false
'Behavior Monitor' = $false
'Office-Anti-Virus' = $true
'NIS' = $false
'Access Protection' = $true
'R-T Protection' = $true
}
foreach($pair in $result.GetEnumerator()) {
# If the Value is $true
if($pair.Value) {
"{0} is Enabled" -f $pair.Key
continue
}
# If we're here last condition was $false
"{0} is Disabled" -f $pair.Key
}
However, since $DefenderStatus seem to be an object you could also do the following (if it was an object[] (array of objects) this would need to be changed):
$DefenderStatus = [PsCustomobject]#{
'Anti-Virus' = $true
'Anti-Malware' = $true
'Anti-Spyware' = $false
'Behavior Monitor' = $false
'Office-Anti-Virus' = $true
'NIS' = $false
'Access Protection' = $true
'R-T Protection' = $true
}
foreach($object in $DefenderStatus.PSObject.Properties) {
# Property Value
if($object.Value) {
"{0} is Enabled" -f $object.Name # Property Name
continue
}
"{0} is Disabled" -f $object.Name
}
Related
$isDebug currently exists as a variable in the CleanStaleDevices function,
I need to make the variable into a optional parameter with a default value of $false.
Add the same parameter to the remaining functions.
I would like to set $isDebug as a variable at the initial function call, where CleanStaleDevices is called, then you can pass the value you set down the chain to the other functions. However, if you want to isolate debugging to an individual function you can override.
I tried converting $idDebug to a function itself but it didn't behave the way I was expecting
`function CleanStaleDevices($numDays) {
##Set debug flag
$isDebug = $true
#######Begin
Connect-AzureAD
###Get-AzureADDevice to get list of devices stale by 3 months and make sure devices aren't autopilot managed###
$dt = (Get-Date).AddDays($numDays)
$devices = Get-AzureADDevice -All:$true | Where-Object { $_.ApproximateLastLogonTimeStamp -le $dt } | select-object -Property AccountEnabled, DeviceId, DeviceOSType, DeviceOSVersion, DisplayName, DeviceTrustType, IsManaged, ApproximateLastLogonTimestamp
$fileNameDate = Get-Date -format "yyyyddMM"
$fileNameMiddle = "_AAD_Stale_Devices-"
##Get Intune devices
$intuneDevicesFileName = "$($fileNameDate)$($fileNameMiddle)Intune.csv"
$devices | ? { $_.IsManaged -eq $true } | Export-Csv c:\temp\$intuneDevicesFileName -NoTypeInformation
#######OUTSIDE ACTION#######
##Use Invoke-Device script to retire devices from generated CSV
##https://www.niallbrady.com/2017/08/23/getting-started-with-microsoft-graph-and-using-powershell-to-automate-things-in-intune/
#######END##################
$confirmation = Read-Host "Have you retired the detected Intune devices (y/n)?"
if ($confirmation -eq 'y') {
$hybridDevicesFileName = "$($fileNameDate)$($fileNameMiddle)Hybrid.csv"
$devices | ? { $_.DeviceTrustType -eq "ServerAd" } | Export-Csv c:\temp\$hybridDevicesFileName -NoTypeInformation
#######OUTSIDE ACTION#######
##Use DisableADComputersFromCSV.ps1 to disable the devices generated in the hybrid device csv
#######END##################
$confirmation = Read-Host "Have you disabled the on prem devices and ran a directory sync (y/n)?"
if ($confirmation -eq 'y') {
# proceed
###We don't have to open a new connection for each loop of the devices
$Connection = New-Object System.Data.SQLClient.SQLConnection
$serverName = "XXXXXXX"
$databaseName = "XXXXXXX"
$Connection.ConnectionString = "server='$serverName';database='$databaseName';trusted_connection=true;"
$Connection.Open()
if ($isDebug -eq $true) {
##debug array
$devicesweredisabled = #()
}
##Sort on DeviceTrustType so that AD Joined devices are disabled before AD registered
$devices = $devices | Sort-Object DeviceTrustType
foreach ($device in $devices) {
#if device is enabled
if ($device.AccountEnabled -eq $true) {
if ($isDebug -eq $true) {
##write to debug array
$devicesweredisabled += $device
Write-Host "Disable device" $device.DeviceId $device.DisplayName $device.AccountEnabled
}
else {
##don't disable Hybrid devices since they were disabled earlier
if ($_.DeviceTrustType -ne "ServerAd")
{
Write-Host "DISABLE " $device.DisplayName "with trust type" $device.DeviceTrustType
#$device.AccountEnabled = $false
}
##Check to see if device is in table already, if so check how long its been there
$disabledDateTimeValue = CheckIfDeviceISInTable $device.DeviceId $Connection
if ($null -eq $disabledDateTimeValue) {
##Device hasn't been added to table
WriteDisabledAADDevice $device.DeviceId $device.DisplayName $isDebug $Connection
}
}
}
else {
#device is not enabled
##Check to see if device is in table already, if so check how long its been there
$disabledDateTimeValue = CheckIfDeviceISInTable $device.DeviceId $Connection
if ($null -eq $disabledDateTimeValue) {
##Device is already disabled so we need to add it to the table for time tracking
WriteDisabledAADDevice $device.DeviceId $device.DisplayName $isDebug $Connection
}
else {
$numDaysSince = (Get-Date) - [datetime]$disabledDateTimeValue | % days
if ($numDaysSince -ge 90) {
if ($isDebug -eq $true) {
Write-Host "DEVICE WAS DELETED" $device.DeviceId $device.DisplayName $device.AccountEnabled
}
else {
Write-Host "DELETED..."
#device has been in table for 90 days so delete
##delete device
}
}
}
}
}
$Connection.Close()
if ($isDebug -eq $true) {
$devicesweredisabled | export-csv c:\temp\devicelist-olderthan-x-days-disabledtest.csv -NoTypeInformation
}
}
else{
Exit
}
}
else{
Exit
}
}
function WriteDisabledAADDevice {
Param(
[parameter(position = 3)]
$conn,
[parameter(position = 2)]
$logOnly,
[parameter(position = 1)]
$displayName,
[parameter(position = 0)]
$deviceId
)
$newId = (New-Guid).Guid
$disableDateTime = (Get-Date)
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $conn
$tableName = "DisabledAADDevice"
$insertquery = "
INSERT INTO $tableName
([DisabledAADDeviceId],[AADDeviceId],[DiplayName],[DisabledDateTime])
VALUES
('$newId','$deviceId','$displayName','$disableDateTime')"
if ($logOnly -eq $true) {
Write-Host $insertquery
}
else {
$Command.CommandText = $insertquery
$Command.ExecuteNonQuery()
}
}
function CheckIfDeviceISInTable {
Param(
[parameter(position = 1)]
$conn,
[parameter(position = 0)]
$deviceId
)
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $conn
$tableName = "DisabledAADDevice"
$selectQuery = "
Select [DisabledDateTime] FROM $tableName
WHERE [AADDeviceId] = '$deviceId'"
$Command.CommandText = $selectQuery
$resultValue = $Command.ExecuteScalar()
return $resultValue
}
CleanStaleDevices(-365)`
I'm trying to convert my CSV file to Excel file with some Table format and style but I'm getting "Cannot index into a null array" for some reason. I'll be really appreciated if I can get any help or suggestion. Thanks
function Convert-to-Excel{
$params = #{
AutoSize = $true
TableStyle = 'Medium6'
BoldTopRow = $true
WorksheetName = 'Audit Log'
PassThru = $true
Path = "C:\AuditLogSearch\$((Get-Date).AddDays(-7).ToString('yyyy-MM-dd')) _ $(Get-Date -Format "yyyy-MM-dd") Audit-Log-Records11.xlsx"
}
$modifiedFile = Import-Csv "C:\AuditLogSearch\Modified-Audit-Log-Records.csv"
$actionReference = Import-Csv "C:\AuditLogSearch\Reference\Action.csv"
$xlsx = foreach ($u in $modifiedFile) {
$u.User = (Get-AzureADUser -ObjectId $u.User).DisplayName
New-Object PsObject -Property #{
User = $u.User
"Search Criteria" = $u."Search Criteria"
"Result Status" = $u."Result Status"
"Date & Time" = $u."Date & Time"
"Type of Action" = if (($actionReference | where-object { $_.Name -eq $u."Type of Action" }).Value -ne $null) { ($actionReference | where-object { $_.Name -eq $u."Type of Action" }).Value }
else { $u."Type of Action" }
} | Export-Excel #params
$ws = $xlsx.Workbook.Worksheets[$params.Worksheetname]
$ws.View.ShowGridLines = $false # => This will hide the GridLines on your file
Close-ExcelPackage $xlsx
}
}
You're closing the Excel Package on the first iteration of your loop hence why when it goes to the next it's trying to do something like this:
$null[$null] # => InvalidOperation: Cannot index into a null array
Try modifying your function so it looks like this instead:
First, construct the object[]:
$result = foreach ($u in $modifiedFile) {
$u.User = (Get-AzureADUser -ObjectId $u.User).DisplayName
New-Object PsObject -Property #{
User = $u.User
"Search Criteria" = $u."Search Criteria"
"Result Status" = $u."Result Status"
"Date & Time" = $u."Date & Time"
"Type of Action" = if (($actionReference.........
else { $u."Type of Action" }
}
}
Then export it to Excel:
$xlsx = $result | Export-Excel #params
$ws = $xlsx.Workbook.Worksheets[$params.Worksheetname]
$ws.View.ShowGridLines = $false # => This will hide the GridLines on your file
Close-ExcelPackage $xlsx
One thing to note, PassThru = $true on the $params means that instead of saving the Excel directly we want to save the object on a variable for "further manipulation" and by further manipulation what I mean is, in this case, hiding the GridLines of the worksheet ($ws.View.ShowGridLines = $false) and then closing the package (store it on the disk).
If you don't require to perform any modifications over the worksheet you can just remove the PassThru altogether and do:
$result | Export-Excel #params
Which will close the package and store the Excel on your disk.
I am trying to create an script to get the certificate expiry date for an websites remotely for multiple servers. I have an script which is working for single server (Need to login into server and doing execution), I need to run this remotely for multiple servers. How can i modify this script to execute for multiple servers remotely. Please advice.
$servers = get-content D:\Certificate.txt
$DaysToExpiration = 60 #change this once it's working
$expirationDate = (Get-Date).AddDays($DaysToExpiration)
foreach ($server in $servers)
{
$sites = Get-Website | ? { $_.State -eq "Started" } | % { $_.Name }
$certs = Get-ChildItem IIS:SSLBindings | ? {
$sites -contains $_.Sites.Value
} | % { $_.Thumbprint }
Get-ChildItem CERT:LocalMachine/My | ? {
$certs -contains $_.Thumbprint -and $_.NotAfter -lt $expirationDate
}
}
Inspired by https://iamoffthebus.wordpress.com/2014/02/04/powershell-to-get-remote-websites-ssl-certificate-expiration/ I use following script:
$minimumCertAgeDays = 60
$timeoutMilliseconds = 10000
$urls = get-content .\check-urls.txt
#disabling the cert validation check. This is what makes this whole thing work with invalid certs...
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
foreach ($url in $urls)
{
Write-Host Checking $url -f Green
$req = [Net.HttpWebRequest]::Create($url)
$req.Timeout = $timeoutMilliseconds
$req.AllowAutoRedirect = $false
try {$req.GetResponse() |Out-Null} catch {Write-Host Exception while checking URL $url`: $_ -f Red}
$certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
#Write-Host "Certificate expires on (string): $certExpiresOnString"
[datetime]$expiration = [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
#Write-Host "Certificate expires on (datetime): $expiration"
[int]$certExpiresIn = ($expiration - $(get-date)).Days
$certName = $req.ServicePoint.Certificate.GetName()
$certPublicKeyString = $req.ServicePoint.Certificate.GetPublicKeyString()
$certSerialNumber = $req.ServicePoint.Certificate.GetSerialNumberString()
$certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
$certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
$certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
if ($certExpiresIn -gt $minimumCertAgeDays)
{
Write-Host Cert for site $url expires in $certExpiresIn days [on $expiration] -f Green
}
else
{
Write-Host WARNING: Cert for site $url expires in $certExpiresIn days [on $expiration] -f Red
Write-Host Threshold is $minimumCertAgeDays days. Check details:`nCert name: $certName -f Red
Write-Host Cert public key: $certPublicKeyString -f Red
Write-Host Cert serial number: $certSerialNumber`nCert thumbprint: $certThumbprint`nCert effective date: $certEffectiveDate`nCert issuer: $certIssuer -f Red
}
Write-Host
rv req
rv expiration
rv certExpiresIn
}
Alternatively, you might find this advanced script useful:
you can switch between report output as Text, Html or PSObject
use the script with urls (parameter array) or with input file for urls or with pipeline input
improved stability: correctly handle missing certificates on HTTP connections
just put the code into a file like Check-ExpiringSslCerts.ps1
Here the advanced script code:
[CmdletBinding(DefaultParametersetname="URLs in text file")]
Param(
[ValidateSet('Text','Html','PSObject')]
[string]$ReportType = 'Text',
[int]$MinimumCertAgeDays = 60,
[int]$TimeoutMilliseconds = 10000,
[parameter(Mandatory=$false,ParameterSetName = "URLs in text file")]
[string]$UrlsFile = '.\check-urls.txt',
[parameter(Mandatory=$false,ParameterSetName = "List of URLs",
ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[string[]]$Urls
)
Begin
{
[string[]]$allUrls = #()
$returnData = #()
[bool]$ProcessedInputPipeLineByArrayItem = $false
function CheckUrl ([string]$url, [array]$returnData)
{
[string]$details = $null
if ($ReportType -eq "Html")
{
$stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($url)
Write-Host "<tr><td>$stringHtmlEncoded</td>"
}
if ($ReportType -eq "Text") { Write-Host Checking $url }
$req = [Net.HttpWebRequest]::Create($url)
$req.Timeout = $timeoutMilliseconds
$req.AllowAutoRedirect = $false
try
{
$req.GetResponse() |Out-Null
if ($req.ServicePoint.Certificate -eq $null) {$details = "No certificate in use for connection"}
}
catch
{
$details = "Exception while checking URL $url`: $_ "
}
if ($details -eq $null -or $details -eq "")
{
$certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
#Write-Host "Certificate expires on (string): $certExpiresOnString"
[datetime]$expiration = [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
#Write-Host "Certificate expires on (datetime): $expiration"
[int]$certExpiresIn = ($expiration - $(get-date)).Days
$certName = $req.ServicePoint.Certificate.GetName()
$certPublicKeyString = $req.ServicePoint.Certificate.GetPublicKeyString()
$certSerialNumber = $req.ServicePoint.Certificate.GetSerialNumberString()
$certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
$certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
$certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
if ($certExpiresIn -gt $minimumCertAgeDays)
{
if ($ReportType -eq "Html")
{
Write-Host "<td>OKAY</td><td>$certExpiresIn</td><td>$expiration</td><td> </td></tr>"
}
if ($ReportType -eq "Text")
{
Write-Host OKAY: Cert for site $url expires in $certExpiresIn days [on $expiration] -f Green
}
if ($ReportType -eq "PSObject")
{
$returnData += new-object psobject -property #{Url = $url; CheckResult = "OKAY"; CertExpiresInDays = [int]$certExpiresIn; ExpirationOn = [datetime]$expiration; Details = [string]$null}
}
}
else
{
$details = ""
$details += "Cert for site $url expires in $certExpiresIn days [on $expiration]`n"
$details += "Threshold is $minimumCertAgeDays days. Check details:`n"
$details += "Cert name: $certName`n"
$details += "Cert public key: $certPublicKeyString`n"
$details += "Cert serial number: $certSerialNumber`n"
$details += "Cert thumbprint: $certThumbprint`n"
$details += "Cert effective date: $certEffectiveDate`n"
$details += "Cert issuer: $certIssuer"
if ($ReportType -eq "Html")
{
Write-Host "<td>WARNING</td><td>$certExpiresIn</td><td>$expiration</td>"
$stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($details) -replace "`n", "<br />"
Write-Host "<tr><td>$stringHtmlEncoded</td></tr>"
}
if ($ReportType -eq "Text")
{
Write-Host WARNING: $details -f Red
}
if ($ReportType -eq "PSObject")
{
$returnData += new-object psobject -property #{Url = $url; CheckResult = "WARNING"; CertExpiresInDays = [int]$certExpiresIn; ExpirationOn = [datetime]$expiration; Details = $details}
}
rv expiration
rv certExpiresIn
}
}
else
{
if ($ReportType -eq "Html")
{
Write-Host "<td>ERROR</td><td>N/A</td><td>N/A</td>"
$stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($details) -replace "`n", "<br />"
Write-Host "<tr><td>$stringHtmlEncoded</td></tr>"
}
if ($ReportType -eq "Text")
{
Write-Host ERROR: $details -f Red
}
if ($ReportType -eq "PSObject")
{
$returnData += new-object psobject -property #{Url = $url; CheckResult = "ERROR"; CertExpiresInDays = $null; ExpirationOn = $null; Details = $details}
}
}
if ($ReportType -eq "Text") { Write-Host }
rv req
return $returnData
}
#disabling the cert validation check. This is what makes this whole thing work with invalid certs...
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
if ($ReportType -eq "Html")
{
Write-Host "<table><tr><th>URL</th><th>Check result</th><th>Expires in days</th><th>Expires on</th><th>Details</th></tr>"
Add-Type -AssemblyName System.Web
}
}
Process
{
if ($_ -ne $null)
{
CheckUrl $_ $returnData
$ProcessedInputPipeLineByArrayItem = $true
}
}
End
{
if ($ProcessedInputPipeLineByArrayItem -eq $false)
{
if ($Urls -eq $null)
{
$allUrls = get-content $UrlsFile
}
else
{
$allUrls = $Urls
}
foreach ($url in $allUrls)
{
$returnData = CheckUrl $url $returnData
}
}
if ($ReportType -eq "Html") { Write-Host "</table>" }
if ($ReportType -eq "PSObject") { return $returnData }
}
Output might look like e.g.:
"http://www.doma.com", "https://www.domb.com" | .\Check-ExpiringSslCerts.ps1 -ReportType PSObject | ft
Url ExpirationOn CertExpiresInDays CheckResult Details
--- ------------ ----------------- ----------- -------
http://www.doma.com ERROR No certificate in use for connection
https://www.domb.com 18.11.2017 09:33:00 87 OKAY
Put the whole code you've wrote in a script-block, in order to do so, just add at the beginning this code:
$sb = {
and at the bottom of your code add:
}
Once you have this script-block, you can run this script on the servers remotely using these commands:
$cred = Get-Credential
$servers = get-content D:\Certificate.txt
Invoke-Command -Credential $cred -ComputerName $servers -ScriptBlock $SB
Hope it helped!
Your code snippets are helpful but they will throw an error on HTTP errors -- that is the nature of the underlying .NET HttpWebRequest object to panic on everything that's not code 200.
So, if you're testing, say, API endpoints that only accept POST HTTP verb, your script will fail as it will get a 405 error message. Alternatively, if your URL returns HTTP 404, your script will fail as well. Luckily, you can catch layer 7 errors and capture the required info anyway just patch your code in this manner:
try {$req.GetResponse() | Out-Null } catch {
if ($_.Exception.InnerException.Status -eq 'ProtocolError') {
# saving the info anyway since this is a L7 error, e.g.:
$certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
[datetime]$expiration [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
$certIssuer = $req.ServicePoint.Certificate.GetIssuerName() # ...
} else {
# this is a real error - timeout, DNS failure etc
Write-Host "$url, $_" -ForegroundColor Red
Continue
}
}
Taken mostly from https://gist.github.com/jstangroome/5945820, although with some changes. The following will obtain the certificate. $certificate.NotBefore and $certificate.NotAfter will then need to be checked.
function GetCertificate([string]$domain, [Int16]$port) {
$certificate = $null
$TcpClient = New-Object -TypeName System.Net.Sockets.TcpClient
$TcpClient.ReceiveTimeout = 1000
$TcpClient.SendTimeout = 1000
try {
$TcpClient.Connect($domain, $port)
$TcpStream = $TcpClient.GetStream()
$Callback = { param($sender, $cert, $chain, $errors) return $true }
$SslStream = New-Object -TypeName System.Net.Security.SslStream -ArgumentList #($TcpStream, $true, $Callback)
try {
$SslStream.AuthenticateAsClient($domain)
$certificate = $SslStream.RemoteCertificate
}
finally {
$SslStream.Dispose()
}
}
finally {
$TcpClient.Dispose()
}
if ($certificate) {
if ($certificate -isnot [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
$certificate = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $certificate
}
}
return $certificate
}
I have a script with a userform with which you can edit an ad user object.
The script runs fine directly on our server but when I try to run it from my system with admin privileges, New-PSSession and a few Invoke-Command's the necessary variables are losing their values.
Here's my code (Some stuff has been censored):
#--------------------#
# Function GUI > GUI #
#--------------------#
Function GUI
{
#---Param
[String]$BadgeListCSV = "link" # -> Badge list
[Bool]$UserChk = $False # -> Indicates if the user has been found
[Bool]$BadgeChk = $False # -> Indicates if the badge is already linked with an ad object
[Int]$BadgeIndex = 1 # -> Badge Nr.
#Global variables
Set-Variable -Name Exit -Value $True -Scope Global
#---Importing badge list
Try
{
$BadgeList = Import-CSV $BadgeListCSV
}
Catch
{
DisplayMSGBox -MSGText "Could not access CSV file." -AddErrorInfo $True | Out-Null
LogFileCleanUp
[System.Environment]::Exit(0)
}
ShowWallBoard
#---Form
$Form = New-Object System.Windows.Forms.Form
$Form.Size = New-Object System.Drawing.Size(184,197)
$Form.Font = New-Object System.Drawing.Font("Segoe UI",9,0,3,1)
$Form.FormBorderStyle = "FixedSingle"
$Form.MaximizeBox = $False
$Form.Text = "S/R Visitor Badge"
$Form.StartPosition = "CenterScreen"
#---GroupBox Badges
$GBBadges = New-Object System.Windows.Forms.GroupBox
$GBBadges.Location = New-Object System.Drawing.Size(10,5)
$GBBadges.Size = New-Object System.Drawing.Size(158,70)
$GBBadges.Text = "Badge"
#Badge choice
$CBBadgeChoice = New-Object System.Windows.Forms.ComboBox
$CBBadgeChoice.Location = New-Object System.Drawing.Size(10,20)
$CBBadgeChoice.Size = New-Object System.Drawing.Size(110)
$CBBadgeChoice.FlatStyle = "PopUp"
$CBBadgeChoice.DropDownStyle = 2
#---Retrieving badge list
Try
{
ForEach($Badge in $BadgeList)
{
$CBBadgeChoice.Items.Add("Visitor Badge $BadgeIndex") | Out-Null
$BadgeIndex++
}
}
Catch
{
DisplayMSGBox -MSGText "Could generate badge choice combobox." -AddErrorInfo $True | Out-Null
LogFileCleanUp
[System.Environment]::Exit(0)
}
$CBBadgeChoice.SelectedIndex = 0
#Unlink
$ChBUnlink = New-Object System.Windows.Forms.CheckBox
$ChBUnlink.Location = New-Object System.Drawing.Size(10,47)
$ChBUnlink.Size = New-Object System.Drawing.Size(135,18)
$ChBUnlink.FlatStyle = "Flat"
$ChBUnlink.Text = "Unlink this badge"
#PictureBox Badge
$PBBadge = New-Object System.Windows.Forms.PictureBox
$PBBadge.Location = New-Object System.Drawing.Size(130,23)
$PBBadge.Size = New-Object System.Drawing.Size(16,16)
$PBBadge.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\cross.PNG")
#---GroupBox User
$GBUser = New-Object System.Windows.Forms.GroupBox
$GBUser.Location = New-Object System.Drawing.Size(10,77)
$GBUser.Size = New-Object System.Drawing.Size(158,53)
$GBUser.Text = "User"
#UserName
$TBUserName = New-Object System.Windows.Forms.TextBox
$TBUserName.Location = New-Object System.Drawing.Size(10,20)
$TBUserName.Size = New-Object System.Drawing.Size(110)
$TBUserName.BorderStyle = 1
#PictureBox UserName
$PBUserName = New-Object System.Windows.Forms.PictureBox
$PBUserName.Location = New-Object System.Drawing.Size(130,23)
$PBUserName.Size = New-Object System.Drawing.Size(16,16)
$PBUserName.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\cross.PNG")
#---Buttons
$BValidate = New-Object System.Windows.Forms.Button
$BValidate.Location = New-Object System.Drawing.Size(10,139)
$BValidate.Size = New-Object System.Drawing.Size(75,23)
$BValidate.FlatStyle = "PopUp"
$BValidate.Text = "Validate"
$BConfirm = New-Object System.Windows.Forms.Button
$BConfirm.Location = New-Object System.Drawing.Size(93,139)
$BConfirm.Size = New-Object System.Drawing.Size(75,23)
$BConfirm.FlatStyle = "PopUp"
$BConfirm.Text = "Confirm"
$BConfirm.Enabled = $False
#---Eventhandling
$DisableTextBox=
{
#---"Unlink this badge" is unchecked
if($ChBUnlink.Checked -eq $False)
{
#---Enable texbox
$TBUserName.Enabled = $True
#---Clear textbox
$TBUserName.Text = ""
#---Set both picture boxes to cross.png
$PBBadge.Image = $PBUserName.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\cross.PNG")
}
#---"Unlink this badge" is checked
if($ChBUnlink.Checked -eq $True)
{
#---Disable textbox
$TBUserName.Enabled = $False
#---Clear textbox
$TBUserName.Text = ""
#---Set the picture box next to the username input textbox to tick.png
$PBUserName.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\tick.PNG")
#---Setting badge check
$BadgeChk = $False
#---Set the picture box next to badge choice combobox to tick.png
$PBBadge.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\cross.PNG")
}
}
#---Buttons On-Click-Actions
$BValidate_OnClick=
{
#---Param
[String]$UserName = $TBUserName.Text # -> Username
[Int]$BadgeCSVID = $CBBadgeChoice.SelectedIndex # -> Index which indicates which badge got selected: Index = 0 -> Visitor Badge 1, Index 1 -> Visitor Badge 2
[Long]$BadgeID = $BadgeList[$BadgeCSVID].ID # -> Getting badge id from csv file
$Form.Enabled = $False
Try
{
#---UserChk
if($UserName -ne "")
{
$UserChk = Invoke-Command -Session $OurADServer -ScriptBlock{Param($UserName) [Bool](Get-ADUser -Filter { sAMAccountName -eq $UserName } -Searchbase "SB")} -ArgumentList $UserName
if($UserChk -eq $False)
{
$PBUserName.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\cross.PNG")
}
ElseIf($UserChk -eq $True)
{
$PBUserName.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\tick.PNG")
}
}
}
Catch
{
DisplayMSGBox -MSGText "Could not validate the user." -AddErrorInfo $True | Out-Null
LogFileCleanUp
[System.Environment]::Exit(0)
}
Try
{
#---BadgeChk
$BadgeChk = Invoke-Command -Session $OurADServer -ScriptBlock{Param($BadgeID) [Bool](Get-ADObject -Filter { Pager -eq $BadgeID })} -ArgumentList $BadgeID
if($BadgeChk -eq $True)
{
#---Retrieving all linked ad objects
$AllLinkedObjectsArray = New-Object System.Collections.ArrayList
$AllLinkedObjects = Invoke-Command -Session $OurADServer -ScriptBlock{Param($BadgeID) Get-ADObject -Filter { Pager -eq $BadgeID }} -ArgumentList $BadgeID
ForEach($Object in $AllLinkedObjects)
{
$AllLinkedObjectsArray.Add($Object.Name) | Out-Null
}
#---Joins array into string with breaks
$AllLinkedObjectsString = $AllLinkedObjectsArray -Join "
"
if($ChBUnlink.Checked -eq $False)
{
$PBBadge.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\cross.PNG")
if((DisplayMSGBox -MSGText ($CBBadgeChoice.SelectedItem + " is already linked with the following objects:`n`n$AllLinkedObjectsString`n`nDo you wish to clear these links?”) -MSGButtons YesNo -MSGIcon Question) -eq "Yes")
{
ReleaseVisitorBadge $AllLinkedObjectsString $BadgeCSVID $BadgeID
}
}
elseif($ChBUnlink.Checked -eq $True)
{
$PBBadge.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\tick.PNG")
}
}
elseif($BadgeChk -eq $False)
{
if($ChBUnlink.Checked -eq $False)
{
$PBBadge.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\tick.PNG")
}
elseif($ChBUnlink.Checked -eq $True)
{
$PBBadge.Image = [System.Drawing.Image]::FromFile("$ScriptPath\images\cross.PNG")
DisplayMSGBox -MSGText ($CBBadgeChoice.SelectedItem + " is currently not in use.") -MSGIcon Information | Out-Null
}
}
}
Catch
{
DisplayMSGBox -MSGText "Could not validate the badge." -AddErrorInfo $True | Out-Null
LogFileCleanUp
[System.Environment]::Exit(0)
}
$Form.Enabled = $True
#---Enabling / disabling the button "Confirm"
if(($BadgeChk -eq $False -and $UserChk -eq $True) -or ($BadgeChk -eq $True -and $ChBUnlink.Checked -eq $True))
{
$BConfirm.Enabled = $True
}
else
{
$BConfirm.Enabled = $False
}
}
$BConfirm_OnClick=
{
if($BadgeChk -eq $False -and $UserChk -eq $True)
{
Set-Variable -Name Exit -Value $False -Scope Global
$Form.Close()
SetVisitorBadge $UserName $BadgeCSVID $BadgeID
}
ElseIf($BadgeChk -eq $True -and $ChBUnlink.Checked -eq $True)
{
Set-Variable -Name Exit -Value $False -Scope Global
$Form.Close()
ReleaseVisitorBadge $AllLinkedObjectsString $BadgeCSVID $BadgeID
}
}
#---Adding Elements to the form
$BValidate.Add_Click($BValidate_OnClick)
$BConfirm.Add_Click($BConfirm_OnClick)
$ChBUnlink.Add_CheckedChanged($DisableTextBox)
$Form.Add_Shown({$Form.Activate(); $TBUserName.Focus()})
$GBBadges.Controls.Add($CBBadgeChoice)
$GBBadges.Controls.Add($PBBadge)
$GBBadges.Controls.Add($ChBUnlink)
$GBUser.Controls.Add($TBUserName)
$GBUser.Controls.Add($PBUserName)
$Form.Controls.Add($GBBadges)
$Form.Controls.Add($GBUser)
$Form.Controls.Add($BValidate)
$Form.Controls.Add($BConfirm)
$Form.ShowDialog()| Out-Null
}
I have two options in my userform. After the user has entered the data, he has to validate it by clicking on "Validate". If everything is correct, the button "Confirm" gets activated and the AD user object may be changed.
However, after validating the data and then clicking on "Confirm" all necessary variables lost their values and I don't know why.
As you can see below, everything is set after validating the data but when I try to confirm it, all values are gone.
The script worked just fine as it is now when run directly on our AD server. All I did was trying to run it via my system by adding a remote session and Invoke-Command's. Could this really be an issue here?
Thanks for any help.
arg1I have some code that creates an empty powershell array, then adds items to this array in a do..while loop. For some reason, no matter what I do, the first element in the array is "true". Why is this?
In my code below, you'll notice the loop adds output a line at a time from an external exe. The exe never returns "true", and you can see I'm doing Write-Host for each it that gets added to the array, and there's never a "true" output from this Write-Host call. There is no other code anywhere in my script that adds any elements to the array. This is frustrating because the index of the array is messed up. This essentially makes this array 1-indexed instead of 0-indexed. Does anyone have any ideas about what's happening here?
Also, one more thing to notice, after I initialize the array, i Write-Host the length, and it's 0, then after I'm dont adding all items, the length is what I would expect it to be if "true" were not the first element. So if there's 4 lines returned from the external app, then the second Write-Host $output.Length call will output 4.
Here is my code:
$output = #()
Write-Host "OUTPUT LENGTH START: $($output.Length)" -ForegroundColor Yellow
$continue = $true
do {
$tempoutput = $Process.StandardOutput.ReadLine()
if(($tempoutput -ne $null) -and ([string]::IsNullOrWhiteSpace($tempoutput) -ne $true)) {
Write-Host $tempoutput -ForegroundColor DarkGray
$output += $tempoutput
} else {
$continue = $false
}
}
while($continue -eq $true)
Write-Host "OUTPUT LENGTH END: $($output.Length)" -ForegroundColor Yellow
Also, at the end of my function, i'm returning the output array like this:
$output
And then in my code that calls my function, if I do a foreach over the returned array's elements, "True" will always be the first item in this array.
EDIT TO INCLUDE FULL FUNCTION
Here is my full function with all the Write-Host calls removed:
function Execute-HttpManager($arguments, $filepath){
$ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo
$ProcessInfo.FileName = $filepath
$ProcessInfo.RedirectStandardError = $true
$ProcessInfo.RedirectStandardOutput = $true
$ProcessInfo.RedirectStandardInput = $true
$ProcessInfo.UseShellExecute = $false
$ProcessInfo.CreateNoWindow = $true
$ProcessInfo.Arguments = $arguments
$Process = New-Object System.Diagnostics.Process
$Process.StartInfo = $ProcessInfo
$Process.Start()
$Process.WaitForExit()
$output = #()
$continue = $true
do {
$tempoutput = $Process.StandardOutput.ReadLine()
if(($tempoutput -ne $null) -and ([string]::IsNullOrWhiteSpace($tempoutput) -ne $true)) {
$output += $tempoutput
} else {
$continue = $false
}
}
while($continue -eq $true)
$deployError = $Process.StandardError.ReadToEnd()
$exitcode = $Process.ExitCode
if($exitcode -eq 4) {
$deployagainresponse = Read-Host
if($deployagainresponse -eq 'y') {
Deploy-Database $localapp $localsqlinstance $localdatabasename $localbackup $localsnapshot
} elseif($deployagainresponse -ne 'bypass') {
throw "http interaction failed with error. Check the output."
}
} elseif ($exitcode -ne 0) {
throw "Database deploy failed."
}
return $output
}
And here is the code that calls my function:
$tokenoutput = Execute-HttpManager #("arg1", "arg2", "arg3", "arg4") $pathtoexecutable
It is this $tokenoutput variable that has the "True" added to the beginning of it's array.
Make sure to check for any calls that are returning a value (such as $Process.Start()), and either precede them with [void] or pipe to out-null: $Process.Start() | Out-Null.
Be aware of this for any function! Anything the function outputs is part of the return value.