Array member name to write-host - powershell

$mbservers = #("server1", "server2")
Foreach ($mbserver in $mbservers) {
#Check for reg key
Invoke-Command -Computername $mbserver -Credential $credsschedtask -ScriptBlock {
$regkey = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\DisableLoopbackCheck"
$checkregkey = (Test-Path $regkey)
If ($checkregkey = "True") {
Write-Host "'DisableLoopbackCheck' key exists on $mbserver".
}
ElseIf ($checkregkey = "False") {
Write-Host "'DisableLoopbackCheck' key does not exist on $mbserver."
}
Else {
Write-Host "Unable to confirm if 'DisableLoopbackCheck' key exists on $mbserver."
}
}
}
I'm trying to workout the Write-Host variable so it display's server1 etc
(Write-Host "'DisableLoopbackCheck' key exists on $mbserver".)
Have tried the following in various ways:
For($i=0; $i -le $mbservers.getupperBound(0);$i++){
write-host $mbservers[$i]
write-host "line 2 :," $mbservers[$i]
I would be grateful for any help.

Your If and ElseIf comparisons don't function at the moment:
= is used for setting values, -eq is used for comparing them
Test-Path doesn't return a string "True", it returns a bool $True
As a bool only has two states $True or $False, you only need If/Else:
Foreach ($mbserver in $mbservers) {
#Check for reg key
Invoke-Command -Computername $mbserver -Credential $credsschedtask -ScriptBlock {
$checkregkey = Test-Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\DisableLoopbackCheck"
If ($checkregkey -eq $True) {
Write-Host "'DisableLoopbackCheck' key exists on $mbserver".
}
Else {
Write-Host "'DisableLoopbackCheck' key does not exist on $mbserver."
}
}
}

Related

powershell v7 - Function pipeline issue

I'm trying to write 2 functions:
the first one (Get-Lab) retrieves a [Lab] object
the second one (remove-Lab) remove a [Lab] object
[Lab] is a class defined in my module.
When a run Get-Lab I correctly retrieve my lab instance with the correct type :
When I run Remove-Lab -Lab (Get-Lab -Name Mylab), the operation is correctly performed:
But when I try to pass the [Lab] object through the pipeline it fails.
The function does not receive the object through the pipeline. However I've set the -Lab Parameter as mandatory with ValueFromPipeline=$true.
Function Remove-Lab{
[CmdletBinding(DefaultParameterSetName='Lab')]
param (
[Parameter(ValueFromPipeline=$true,ParameterSetName='Lab',Position=0,Mandatory=$true)]
[Lab]
$Lab,
# Parameter help description
[Parameter(Position=1,Mandatory=$false)]
[switch]
$Force=$false
)
begin {
Write-host ("`tLabName : {0}" -f $Lab.Name) -ForegroundColor Yellow
if ($null -ne $Lab) {
$LabToRemove = $Lab
}
if (-not [string]::IsNullOrEmpty($LabId)) {
$LabToRemove = Get-Lab -Id $LabId
}
if (-not [string]::IsNullOrEmpty($Name)) {
$LabToRemove = Get-Lab -Name $Name
}
if ($null -eq $LabToRemove) {
throw "There is no Lab with specified characteristics. Please check your input"
}
}
process {
$DoRemoval = $true
if ($Force.IsPresent -eq $false) {
while ($null -eq $UserInput -or $UserInput -notin #('Y','N')) {
$UserInput = Read-HostDefault -Prompt "Are you sure want to remove the selected Lab and all its components ? [Y]es, [N]o" -Default 'N'
if ($UserInput -eq 'N') {
$DoRemoval = $false
}
}
Write-Host ("`tUser Input : {0}" -f $UserInput) -ForegroundColor Green
}
if ($DoRemoval -eq $true) {
Write-Host ("`tAbout to Remove the following Lab : {0}" -f $LabToRemove.Name) -ForegroundColor Green
}
}
end {
}
}
As you can see below when a debug this function, the $Lab Parameter is null.
Do you have any idea about this issue ?
Since the function is testing on $LabId or $Name, these variables need to exist in the function and at the moment they do not.
Try changing the parameters to:
[CmdletBinding(DefaultParameterSetName='LabId')]
param (
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName = $true, ParameterSetName='LabId',Position=0,Mandatory=$true)]
[string]$LabId,
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName = $true, ParameterSetName='LabName',Position=0,Mandatory=$true)]
[string]$Name,
# Parameter help description
[switch]$Force # no need to set a switch to $false because if you don't send that param, the undelying value will be $false by default
)
Then remove
Write-host ("`tLabName : {0}" -f $Lab.Name) -ForegroundColor Yellow
if ($null -ne $Lab) {
$LabToRemove = $Lab
}
Important part here is the ValueFromPipelineByPropertyName = $true declaration
begin runs before anything else, including pipeline parameter binding - so you need to move code that inspects a pipeline-bound parameter (like $Lab) to the process block:
Function Remove-Lab{
[CmdletBinding(DefaultParameterSetName='Lab')]
param (
[Parameter(ValueFromPipeline=$true,ParameterSetName='Lab',Position=0,Mandatory=$true)]
[Lab]
$Lab,
# Parameter help description
[Parameter(Position=1,Mandatory=$false)]
[switch]
$Force=$false
)
process {
Write-host ("`tLabName : {0}" -f $Lab.Name) -ForegroundColor Yellow
if ($null -ne $Lab) {
$LabToRemove = $Lab
}
if (-not [string]::IsNullOrEmpty($LabId)) {
$LabToRemove = Get-Lab -Id $LabId
}
if (-not [string]::IsNullOrEmpty($Name)) {
$LabToRemove = Get-Lab -Name $Name
}
if ($null -eq $LabToRemove) {
throw "There is no Lab with specified characteristics. Please check your input"
}
$DoRemoval = $true
if ($Force.IsPresent -eq $false) {
while ($null -eq $UserInput -or $UserInput -notin #('Y','N')) {
$UserInput = Read-HostDefault -Prompt "Are you sure want to remove the selected Lab and all its components ? [Y]es, [N]o" -Default 'N'
if ($UserInput -eq 'N') {
$DoRemoval = $false
}
}
Write-Host ("`tUser Input : {0}" -f $UserInput) -ForegroundColor Green
}
if ($DoRemoval -eq $true) {
Write-Host ("`tAbout to Remove the following Lab : {0}" -f $LabToRemove.Name) -ForegroundColor Green
}
}

Only prints the last server in the list, I want all servers

This only prints the last server in the list, I'm looking to get all servers and print to screen
$machines = (Get-BrokerMachine -AdminAddress $adminaddress -DesktopGroupName $deliverygroup | Select-Object DNSname).DNSname
foreach($machine in $machines){
$machinelist = Get-BrokerMachine -HostedMachineName $machine
if($machinelist.InMaintenanceMode -eq $true){
$status = "$machine is in maintenance mode"
}else {
$status = "$machine is not in maintenance mode"
}
}
Write-Host $status
Here is a more PowerShell-like approach (not tested):
Get-BrokerMachine -AdminAddress $adminaddress -DesktopGroupName $deliverygroup | ForEach-Object {
$machineName = $_.DNSName
[PSCustomObject] #{
"MachineName" = $machineName
"MaintenanceMode" = (Get-BrokerMachine -HostedMachineName $machine).InMaintenanceMode
}
} | Export-Csv "C:\whatever\results.csv" -NoTypeInformation
$Status is constantly being overwritten by the current machine in your list.
You're looking for:
$Status+=
As opposed to:
$Status=
You'll also want to explicitly state that $Status will be an array at the beginning like so:
$Status=#()
Or when you create the variable and omit the line at the beginning.
[array]$Status +=
Otherwise, you'll get results that run together as it will be treated as a [String]
another funky mode :
function get-BrokerMachineMode
{
param (
[Parameter(Mandatory = $true)]
[string[]]$machines
)
begin
{
$ErrorActionPreference = 'Stop'
Add-Type -Language CSharp #"
public class BrokenBroker {
qpublic System.String MachineName;
public System.String MaintenanceMode;
public BrokenBroker (string MachineName, string MaintenanceMode)
{
this.MachineName = MachineName;
this.MaintenanceMode = IsInMaintenanceMode;
}
}
"#
$status = #()
Write-Verbose "Created objects..."
}
process
{
try
{
$machines = (Get-BrokerMachine -AdminAddress $adminaddress `
-DesktopGroupName $deliverygroup | Select-Object DNSname).DNSname
foreach ($machine in $machines)
{
Write-Verbose "Checking machine: $machine"
$machinelist = Get-BrokerMachine -HostedMachineName $machine
if ($machinelist.InMaintenanceMode -eq $true)
{
$status += New-Object BrokenBroker($machine, $true)
}
else
{
$status += New-Object BrokenBroker($machine, $false)
}
}
}
catch
{
Write-Error $error[0].Exception.Message
}
$status
}
end
{
Write-Verbose "Done"
}
}
this is a function you just must to load then you can launch it just by using this command:
$computers = get-content = {PATH TO TXT FILE}
$list = get-BrokerMachineMode -machines $computers -Verbose

Using RunAs in PowerShell

I have the following PowerShell script to run a quick HitmanPro scan on computers. I have deployed this script through GFI RMM and while it works, I need to pass it domain administrator information.
$HitmanParameters = "/scanonly /quick /noinstall /quiet"
$AgentPath = (${env:ProgramFiles(x86)}, ${env:ProgramFiles} -ne $null)[0]
if (Test-Path "$AgentPath\Advanced Monitoring Agent") {
$AgentPath="$AgentPath\Advanced Monitoring Agent"
} elseif (Test-Path "$AgentPath\Advanced Monitoring Agent GP") {
$AgentPath="$AgentPath\Advanced Monitoring Agent GP"
} else {
Write-Host "Agent Path Not Found"
}
if (-not (Test-Path "$AgentPath\HitmanPro")) {
Write-Host "Creating Hitman Folder"
[system.io.directory]::CreateDirectory("$AgentPath\HitmanPro")
}
$HitmanParameters = "$HitmanParameters /log=""$AgentPath\HitmanPro\results.txt"""
if (-not (Test-Path "$AgentPath\HitmanPro\HitmanPro.exe")) {
Write-Host "HitmanPro Not found"
if ($(Get-WmiObject -Query "SELECT * FROM Win32_Processor WHERE AddressWidth='64'")) {
$HMPURL = "http://dl.surfright.nl/HitmanPro_x64.exe"
} else {
$HMPURL = "http://dl.surfright.nl/HitmanPro.exe"
}
Write-Host "Downloading $HMPURL ..."
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($HMPURL,"$AgentPath\HitmanPro\HitmanPro.exe")
if (Test-Path "$AgentPath\HitmanPro\HitmanPro.exe") {
Write-Host "Download Complete!"
} else {
Write-Host "Error Downloading Hitman Pro"
}
}
if (Test-Path "$AgentPath\HitmanPro\HitmanPro.exe") {
Write-Host "Running HitmanPro Scan..."
Write-Host "HitmanPro.exe $HitmanParameters"
Start-Process -FilePath "$AgentPath\HitmanPro\HitmanPro.exe" -ArgumentList "$HitmanParameters" -NoNewWindow -Wait
If (Test-Path "$AgentPath\HitmanPro\results.txt") {
$data = Get-Content -Encoding Unicode "$AgentPath\HitmanPro\results.txt"
remove-item "$AgentPath\HitmanPro\results.txt"
foreach ($line in $data) {
If($line.contains("Threats")) {
$Infections = [int] $line.Split(":")[1]
If($Infections -eq 0) {
Write-Host "No Infections Found"
$data
exit 0
break
} Else {
Write-Host "Infections Found!"
$data
exit 1000
break
}
} #If Line contains Threats
} #ForEach
} Else {
Write-Host "No Logfile Found"!
} #LogFile Found
} Else {
Write-Host "HitmanPro Still Not Found!"
}#HitmanPro Found

Change the evaluator within a switch statement

Is there a way, in a 'switch' to modify the variable you are evaluating on, and get it to change the match?
$var = "a"
switch ($var){
"a" {Write-Host "1st match for 'a'"}
"b" {Write-Host "1st match for 'b'"}
"a" {Write-Host "2nd match for 'a'"; $var = "b" ; continue}
"b" {Write-Host "2st match for 'b'"}
}
I would love to be able to get the above to match:
1st match for 'a'
2nd match for 'a'
2nd match for 'b'
$Destination = "vmstores:\vcsa#443\Training\Local-B\David" #is a param of my function, which could be a bunch of different types of object.
$DestinationType = $Destination.GetType().Name
switch ($DestinationType){
"String" {
if ((Test-Path $Destination) -eq $true){
if ((Get-Item $Destination).GetType().Name -eq "DatastoreFolderImpl"){
$DestinationType = "DatastoreFolderImpl"
if ((Test-Path $Destination.Insert($Destination.Length,"\").Insert(($Destination.Length)+1,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
}
elseif ((Get-Item $Destination).GetType().Name -eq "DatastoreFileImpl"){
$DestinationType = "DatastoreFileImpl"
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
}
; continue
}
"DirectoryInfo" {
if ((Test-Path $Destination.Insert($Destination.Length,"\").Insert(($Destination.Length)+1,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
; break
}
"FileInfo" {
if ((Test-Path $Destination) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
; break
}
"DatastoreFileImpl" {
if ((Test-Path $Destination) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
; break
}
"DatastoreFolderImpl" {
if ((Test-Path $Destination.Insert($Destination.Length,"\").Insert(($Destination.Length)+1,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
; break
}
"NasDatastoreImpl" {
New-PSDrive -Name "DestMount" -Root \ -PSProvider VimDatastore -Datastore $Destination | Out-Null
if ((Test-Path ("DestMount:").insert(10,"\").Insert(11,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
$Destination = ("DestMount:").insert(10,"\")
; break
}
"VMFSDatastoreImpl" {
New-PSDrive -Name "DestMount" -Root \ -PSProvider VimDatastore -Datastore $Destination | Out-Null
if ((Test-Path ("DestMount:").insert(10,"\").Insert(11,$SourceVMXShort)) -eq $true){
Write-Warning "Destination File exists..."
$DestinationExists = $true
}
$Destination = ("DestMount:").insert(10,"\")
; break
}
}
As you can see, it would be more elegant if i could update the $DestinationType so i could re-use the statement in the other switch blocks, rather than the extra 'ifs'
While I don't think you can change the switch statements variable mid check, you can do nested switch statements. This code gives your requested output:
$var = "a"
switch ($var){
"a" {Write-Host "1st match for 'a'"}
"b" {Write-Host "1st match for 'b'"}
"a" {Write-Host "2nd match for 'a'"
$var = "b"
switch ($var) {
"b" {Write-Host "2st match for 'b'"}
} #End sub-switch
} #End "A" check
} #End Primary switch
Now, I'm not sure what your overall goal is, there might be better ways to do this with functions and such.
Edit after updated question and comments:
From looking at the updated code, you can pull the one if statement that sets $DestinationExists = $true. You might be able to reorganize your if statements so they only show up once. Unfortunately, there is no way to get the switch statement to change the variable mid-switch. From your comment, maybe you can add an extra parameter that will dictate the type, so you can us one large switch statement based off the type. Something like this:
switch ($DataType) {
"String" {<#Things to do if string#>}
"OtherTypes" {<#Continue like this#>}
}#End Switch ($DataType)
I think at that point though, I would start using Parameter Sets. Here is a blog describing about Parameter Sets
Here is a somewhat convoluted way to do it. Note that the example code is similar but not the same as the code in OP.
add-type #"
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Management.Automation;
public class PSVarEnumeration : IEnumerable
{
private ScriptBlock _getterScript;
public PSVarEnumeration(ScriptBlock getterScript)
{
_getterScript = getterScript;
}
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator) GetEnumerator();
}
public PSVarEnumerator GetEnumerator()
{
return new PSVarEnumerator(_getterScript);
}
}
public class PSVarEnumerator : IEnumerator
{
private ScriptBlock _getterScript;
bool areDone = false;
// position isn't used for anything
int position = -1;
public PSVarEnumerator(ScriptBlock getterScript)
{
_getterScript = getterScript;
}
public bool MoveNext()
{
if (!areDone) {
if (Current != null) {
position++;
return true;
} else {
areDone = true;
return false;
}
} else {
return false;
}
}
public void Reset()
{
position = -1;
areDone = false;
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public Object Current
{
get
{
Collection<PSObject> results = _getterScript.Invoke();
return results[0];
}
}
}
"#
$mySwitcher ="a"
$mySwitcherScript = {$mySwitcher}
$var = new PSVarEnumeration ( $mySwitcherScript)
switch ($var){
"a" {Write-Host "1st match for 'a'"}
"b" {Write-Host "1st match for 'b'"}
"a" {Write-Host "2nd match for 'a'"; $mySwitcher = "b" ; continue}
"b" {Write-Host "2nd match for 'b'"; $mySwitcher = $null}
}
<#
Output I get is:
1st match for 'a'
2nd match for 'a'
1st match for 'b'
2nd match for 'b'
#>

Troubleshooting if in powershell

I am in a very strange powershell behavior.
I have an If, elseif, else.
The If works, The else if works, but the else never happened.
What could it be?
This is the script:
$result = Get-AMTHardwareAsset $Computer -credential $VProCredential -textoutput
if ($result.Contains("Unauthorized") -eq $true)
{
Add-Results "$Computer`tHardwareAssets-Unauthorized"
}
elseif ($result.Contains("Could not connect") -eq $true)
{
Add-Results "$Computer`tHardwareAssets-CannotConect"
}
else
{
Add-Results "TESTING"
}
Changing the script to hardcode $result to "Fubar", and substituting Write-Host for Add-Results:
$result = 'Fubar'; #Get-AMTHardwareAsset $Computer -credential $VProCredential -textoutput
if ($result.Contains("Unauthorized") -eq $true)
{
#Add-Results "$Computer`tHardwareAssets-Unauthorized"
Write-Host "$Computer`tHardwareAssets-Unauthorized"
}
elseif ($result.Contains("Could not connect") -eq $true)
{
#Add-Results "$Computer`tHardwareAssets-CannotConect"
Write-Host "$Computer`tHardwareAssets-CannotConect"
}
else
{
#Add-Results "TESTING"
Write-Host "TESTING"
}
and I get "TESTNG".
Some context you have not included applies, without it the only answer you will get is: it looks OK