I am trying to make a script that will check the BitLocker status automatically, and then send an email if it is not enabled.
Here is what I have so far:
Get-BitlockerVolume -MountPoint "C:" | Select ProtectionStatus
That shows me the status, but now I am struggling to process the output. I've tried doing it like this:
$OutputVariable = (Get-BitlockerVolume -MountPoint "C:" | Select
ProtectionStatus)
If ($OutputVariable -like "Off") {Echo "Oops"}
Else {Echo "Wow!"}
Which should output me "Oops" if I'm understanding it correctly, but it keeps showing me "Wow!"
Maybe I am doing it wrong, so I'm looking for some guidance.
Edit:
Thanks to the comments below, I was able to make it work. Here's my full script:
# Bitlocker Script
set-alias ps64 "$env:windir\sysnative\WindowsPowerShell\v1.0\powershell.exe"
set-alias ps32 "$env:windir\syswow64\WindowsPowerShell\v1.0\powershell.exe"
ps64 {Import-Module BitLocker; Get-BitlockerVolume}
$wmiDomain = Get-WmiObject Win32_NTDomain -Filter "DnsForestName = '$( (Get-WmiObject Win32_ComputerSystem).Domain)'"
$domain = $wmiDomain.DomainName
$OutputVariable = (ps64 {Get-BitlockerVolume -MountPoint "C:"})
If ($OutputVariable.volumestatus -like "FullyEncrypted")
{
Exit
}
ElseIf ($OutputVariable.volumestatus -NotLike "FullyEncrypted")
{
$date = Get-Date
$emailSmtpServer = "smtp.xxx.com"
$emailSmtpServerPort = "xxx"
$emailSmtpUser = "xxx#xxx.nl"
$emailSmtpPass = "xxx"
$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = "Report <xxx#xxx.nl>"
$emailMessage.To.Add( "xxx#xxx.net" )
$emailMessage.Subject = "Bitlocker Status Alert | $domain $env:COMPUTERNAME"
$emailMessage.Body = "Bitlocker niet actief op $domain $env:COMPUTERNAME getest op $date"
$SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer , $emailSmtpServerPort )
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential( $emailSmtpUser , $emailSmtpPass );
$SMTPClient.Send( $emailMessage)
}
PowerShell returns objects. You use the Select cmdlet to reduce the properties of those objects to ones you're interested in.
As such the following command:
Get-BitlockerVolume -MountPoint "C:" | Select ProtectionStatus
Returns an object with a single "ProtectionStatus" property and as a result comparing that to a string does not result in a match.
You can instead access the property via dot notation (e.g $OutputVariable.protectionstatus) to perform a comparison on its content. Alternatively, you could modify your Select cmdlet to use -ExpandProperty which will return the value of the specified property as an object of its type:
$OutputVariable = Get-BitlockerVolume -MountPoint "C:" | Select -ExpandProperty ProtectionStatus
Another way to achieve the same result would be:
$OutputVariable = (Get-BitlockerVolume -MountPoint "C:").ProtectionStatus
Here the brackets make the cmdlet execute, but then we use dot notation to just return the specified property.
$OutputVariable = (Get-BitlockerVolume -MountPoint "C:")
If ($OutputVariable.protectionstatus -like "Off")
{
Write-Output "Oops"
}
Else
{
Write-Output "Wow!"
}
Try this
Related
I have written a below script which prompts user to enter a remote computer name, checks "HideFastUserSwitching" value in the registry of the computer and changes it if user wishes so.
The script does its job, I have tested to change the value from 1 to 0 on one machine. But if I run the script again and try to change it back to 1, it doesn't do that. The value stays 0.
Could you please help me figure out why does this happen? If you have remarks about other pieces of code in this script, you are welcome to give me some advise, it will be appreciated.
$PN = (Read-Host "Enter PN#").ToUpper().Trim()
$path = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System"
$regKeyName = "HideFastUserSwitching"
$hideFastUserSwitchingValue = $null
Function getHideFastUserSwitchingValue {
$regKeyValue = $null
$parameters = #{
ComputerName = $PN
ScriptBlock = {
Param ($path, $regKeyName)
Get-ItemProperty -Path $path -Name $regKeyName | Select-Object -ExpandProperty $regKeyName
}
ArgumentList = $path, $regKeyName
}
Invoke-Command #parameters
}
Function setHideFastUserSwitchingValue($value) {
$parameters = #{
ComputerName = $PN
ScriptBlock = {
Param ($path, $regKeyName)
Set-ItemProperty -Path $path -Name $regKeyName -Value $value
}
ArgumentList = $path, $regKeyName
}
Invoke-Command #parameters
}
$hideFastUserSwitchingValue = getHideFastUserSwitchingValue
$yesNo = (Read-Host "HideFastUserSwitching value is $hideFastUserSwitchingValue. Do you want to modify it [y/n]?").ToUpper().Trim()
if($yesNo -eq "Y") {
$input = Read-Host "Enter the new value"
setHideFastUserSwitchingValue($input)
$hideFastUserSwitchingValue = getHideFastUserSwitchingValue
Write-Host "HideFastUserSwitching value is now $hideFastUserSwitchingValue"
} else {
$hideFastUserSwitchingValue = getHideFastUserSwitchingValue
Write-Host "HideFastUserSwitching value has not been modified and is equal to $hideFastUserSwitchingValue"
}
I'm trying to expose an endpoint using RestPS routes in powershell. I was able to expose it and run a custom PS script when an endpoint (http://localhost:8080/scan) is hit.
How do we pass body through routes & based on that value I need to execute the custom script.
RestPSroutes.json - Example
{
"RequestType": "GET",
"RequestURL": "/scan",
"RequestCommand": "C:/RR/api-compliance.ps1" }
snippet from the above script (api-compliance.ps1) ;
###############
# Setup Creds #
###############
$userID = "****"
$Pass = "*****"
#$Creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userID,$Pass
$vCenter = "vCenter01"
$cluster = "Cluster01"
$vmhost = "Host01"
$bl = "Baseline01"
######################
# Connect to vCenter #
######################
Connect-VIServer $VCTRs -user $userID -password $Pass -WarningAction SilentlyContinue -Force
$baseline = Get-Baseline | ? {$_.name -eq $bl}
$baseline | Attach-Baseline -Entity $vmhost -Confirm:$false
Scan-Inventory -Entity $vmhost
Right now we are hardcoding the below values
$vCenter = "vCenter01"
$cluster = "Cluster01"
$vmhost = "Host01"
But we want these to be passed as a request body. Can someone help?
Simply declare parameters $RequestArgs and $Body in the target script/function, and RestPS will automatically pass the appropriate values along as strings:
param(
[string]$RequestArgs,
[string]$Body
)
$RequestArgs.Split('&') |ForEach-Object {
$paramName,$paramValue = $_.Split('=')
Write-Host "Received query parameter ${paramName} with value '$paramValue'"
}
I've been browsing the web trying to find a way if possible to email a low disk space alert from a Gmail account to a shared mail box using power shell but Im struggling with a query I've managed to piece together.
$EmailFrom = "FromEmail#Gmail.com"
$EmailTo = "ToEmail#Gmail.com"
$SMTPServer = "smtp.gmail.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("Username", "Password");
$Computers = "Local Computer"
$Subject = "Disk Space Storage Report"
$Body = "This report was generated because the drive(s) listed below have less than $thresholdspace % free space. Drives above this threshold will not be listed."
[decimal]$thresholdspace = 50
$tableFragment = Get-WMIObject -ComputerName $computers Win32_LogicalDisk `
| select __SERVER, DriveType, VolumeName, Name, #{n='Size (Gb)' ;e={"{0:n2}" -f ($_.size/1gb)}},#{n='FreeSpace (Gb)';e={"{0:n2}" -f ($_.freespace/1gb)}}, #{n='PercentFree';e={"{0:n2}" -f ($_.freespace/$_.size*100)}} `
| Where-Object {$_.DriveType -eq 3} `
| ConvertTo-HTML -fragment
$regexsubject = $Body
$regex = [regex] '(?im)<td>'
if ($regex.IsMatch($regexsubject)) {$smtpclinet.send($fromemail, $EmailTo, $Subject, $Body)}
Script runs but nothing happens, any help would be fantastic!!!
My version would be longer because I'd have made a substitute for Send-MailMessage such that swapping between mine and Send-MailMessage is trivial.
This is one possible way of doing it. There are good uses for the Fragment parameter on ConvertTo-Html, but not much of a justification to do so here.
This is a script and expected to be a .ps1 file. Mandatory things I don't really want to hard-code beyond a default are set in the param block.
param(
[String[]]$ComputerName = $env:COMPUTERNAME,
[Decimal]$Theshold = 0.5,
[PSCredential]$Credential = (Get-Credential)
)
#
# Supporting functions
#
# This function acts in much the same way as Send-MailMessage.
function Send-SmtpMessage {
param(
[Parameter(Mandatory = $true, Position = 1)]
[String[]]$To,
[Parameter(Mandatory = $true, Position = 2)]
[String]$Subject,
[String]$Body,
[Switch]$BodyAsHtml,
[String]$SmtpServer = $PSEmailServer,
[Int32]$Port,
[Switch]$UseSSL,
[PSCredential]$Credential,
[Parameter(Mandatory = $true)]
[String]$From
)
if ([String]::IsNullOrEmtpy($_)) {
# I'd use $pscmdlet.ThrowTerminatingError for this normally
throw 'A value must be provided for SmtpServer'
}
# Create a mail message
$mailMessage = New-Object System.Net.Mail.MailMessage
# Email address formatting si validated by this, allowing failure to kill the command
try {
foreach ($recipient in $To) {
$mailMessage.To.Add($To)
}
$mailMessage.From = $From
} catch {
$pscmdlet.ThrowTerminatingError($_)
}
$mailMessage.Subject = $Subject
$mailMessage.Body = $Body
if ($BodyAsHtml) {
$mailMessage.IsBodyHtml = $true
}
try {
$smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port)
if ($UseSSL) {
$smtpClient.EnableSsl = $true
}
if ($psboundparameters.ContainsKey('Credential')) {
$smtpClient.Credentials = $Credential.GetNetworkCredential()
}
$smtpClient.Send($mailMessage)
} catch {
# Return errors as non-terminating
Write-Error -ErrorRecord $_
}
}
#
# Main
#
# This is inserted before the table generated by the script
$PreContent = 'This report was generated because the drive(s) listed below have less than {0} free space. Drives above this threshold will not be listed.' -f ('{0:P2}' -f $Threshold)
# This is a result counter, it'll be incremented for each result which passes the threshold
$i = 0
# Generate the message body. There's not as much error control around WMI as I'd normally like.
$Body = Get-WmiObject Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $ComputerName | ForEach-Object {
# PSCustomObject requires PS 3 or greater.
# Using Math.Round below means we can still perform numeric comparisons
# Percent free remains as a decimal until the end. Programs like Excel expect percentages as a decimal (0 to 1).
[PSCustomObject]#{
ComputerName = $_.__SERVER
DriveType = $_.DriveType
VolumeName = $_.VolumeName
Name = $_.Name
'Size (GB)' = [Math]::Round(($_.Size / 1GB), 2)
'FreeSpace (GB)' = [Math]::Round(($_.FreeSpace / 1GB), 2)
PercentFree = [Math]::Round(($_.FreeSpace / $_.Size), 2)
}
} | Where-Object {
if ($_.PercentFree -lt $Threshold) {
$true
$i++
}
} | ForEach-Object {
# Make Percentage friendly. P2 adds % for us.
$_.PercentFree = '{0:P2}' -f $_.PercentFree
$_
} | ConvertTo-Html -PreContent $PreContent | Out-String
# If there's one or more warning to send.
if ($i -gt 0) {
$params = #{
To = "ToEmail#Gmail.com"
From = "FromEmail#Gmail.com"
Subject = "Disk Space Storage Report"
Body = $Body
SmtpServer = "smtp.gmail.com"
Port = 587
UseSsl = $true
Credential = $Credential
}
Send-SmtpMessage #params
}
I'm writing a powershell script that searches for users inside an Active Directory OU and allows me to reset passwords by choosing matches from a list. I found a Tutorial that uses the System.DirectoryServices.DirectoryEntry and System.DirectoryServices.DirectorySearcher, and modified it like so:
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP:\\[REDACTED]")
##ReadSTDIN
$strSearch = Read-Host -Prompt "Search"
$strCat = "(&(objectCategory=User)(Name=*" + $strSearch + "*))"
## Search Object
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strCat
$objSearcher.SearchScope = "Subtree"
#Load Required Properties into the dynObjLink
$objSearcher.PropertiesToLoad.Add("name")
$objSearcher.PropertiesToLoad.Add("userPrincipalName")
$objSearcher.PropertiesToLoad.Add("SamAccountName")
##Magical Search Function
$colResults = $objSearcher.FindAll()
$colResults.PropertiesLoaded
#for every returned userID add them to a table
ForEach ($objResult in $colResults)
{$a++
$objResult.count
$objItem = $objResult.Properties
$objItem.name
$objItem.userPrincipalName
$results.Add($a, $objItem.name + $objItem.userPrincipalName + $objItem.SamAccountName)
}
#Print Table
$results | Format-Table -AutoSize
This works well enough, but when it prints data I can only get the "first name" value of anything that comes back. Everything else becomes NULL and I can't figure out why.
Name Value
---- -----
3 {James3 [REDACTED], $null, $null}
2 {James2 [REDACTED], $null, $null}
1 {James1 [REDACTED], $null, $null}
I've tried different kinds of authentication and manipulating values, but the DirectorySearcher object only seems to collect the "name" value of any record it returns, no matter what I load into it. Help?
Here's a bit shorter (and PowerShell v2-compatible) way of doing this:
#requires -version 2
param(
[Parameter(Mandatory=$true)]
[String] $SearchPattern
)
$searcher = [ADSISearcher] "(&(objectClass=user)(name=$SearchPattern))"
$searcher.PageSize = 1000
$searcher.PropertiesToLoad.AddRange(#("name","samAccountName","userPrincipalName"))
$searchResults = $searcher.FindAll()
if ( $searchResults.Count -gt 0 ) {
foreach ( $searchResult in $searchResults ) {
$properties = $searchResult.Properties
$searchResult | Select-Object `
#{Name = "name"; Expression = {$properties["name"][0]}},
#{Name = "sAMAccountName"; Expression = {$properties["samaccountname"][0]}},
#{Name = "userPrincipalName"; Expression = {$properties["userprincipalname"][0]}}
}
}
$searchResults.Dispose()
Note that there's no need to build a list and output afterwards. Just output each search result. Put this code in a script file and call it:
PS C:\Scripts> .\Searcher.ps1 "*dyer*"
If you omit the parameter, PowerShell will prompt you for it (because the parameter is marked as mandatory).
try using Properties matching to the PropertiesToLoad
$entry = new-object -typename system.directoryservices.directoryentry -ArgumentList $LDAPServer, "ldap", "esildap"
$entry.Path="LDAP://OU=childOU,OU=parentOU,DC=dc1,DC=dc2"
$searcher = new-object -typename system.directoryservices.directorysearcher -ArgumentList $entry
$searcher.PropertiesToLoad.Add('samaccountname')
$searcher.PropertiesToLoad.Add('mail')
$searcher.PropertiesToLoad.Add('displayname')
$objs = $searcher.findall()
foreach($data in $objs)
{
$samaccountname = $data.properties['samaccountname'][0] + ''
$mail = $data.properties['mail'][0] + ''
$displayname = $data.properties['displayname'][0] + ''
}
when accessing the properties of the resultset you get a System.DirectoryServices.ResultPropertyValueCollection type for each property
to get a string value for passing to a database the property value access the zero index of the object
I have a script which returns the location of a list of computers within AD. This works fine for my current domain and if I specify "-searchroot" it works for the others.
If I query a computer not on the correct domain, with 'get-qadcomputer' it does not return any information to the console, so need a method to catch and try alternatives?
I have tried this, which just works for all computer in DC=Domain1 (my current domain):
Add-PSSnapin Quest.ActiveRoles.ADManagement
$strPath = "C:\sample.xlsx"
$objExcel = new-object -ComObject Excel.Application
$objExcel.Visible = $false
$WorkBook = $objExcel.Workbooks.Open($strPath)
$worksheetIn = $workbook.sheets.item("Asset")
$worksheetOut = $workbook.sheets.item("Asset")
$intRowMax = ($worksheetIn.UsedRange.Rows).count
$Columnnumber = 1
For($intRow = 2 ; $intRow -le $intRowMax ; $intRow++) {
Try
{
$name = $worksheetIn.cells.item($intRow,$ColumnNumber).value2
"Querying $name...$introw of $intRowMax"
$OU = Get-QADcomputer -searchroot 'OU=Workstations,DC=DOMAIN1'-LdapFilter "(CN=$Name)"`
| ft Location -HideTableHeaders
$Out = Format-list -InputObject $OU | out-string
$worksheetOut.cells.item($intRow,6) = "$Out"
}
Catch
{
$name = $worksheetIn.cells.item($intRow,$ColumnNumber)
"Querying $name...$introw of $intRowMax"
$OU = Get-QADComputer -SearchRoot 'OU=Workstations,DC=DOMAIN2' -LdapFilter (CN=$Name)"`
| ft Location -HideTableHeaders
$Out = Format-list -InputObject $OU | out-string
$worksheetOut.cells.item($intRow,6) = "$Out"
}
}
$objexcel.save()
$objExcel.workbooks.close()
$objexcel.application.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Workbook)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objExcel)
Remove-Variable objExcel
You could check if $OU is equal to $null. I suspect it just isn't finding anything if you get no error text or it doesn't take you to the catch handler. Another option is to check $? right after the line calling Get-QADComputer but I suspect you will get True (command succeeded). But if you get False, then that tells you it didn't succeed.