Conditionally change boolean variable in an imported CSV - csv

I am working on a script and I want to compare an array to another array, and change a boolean value (from $false to $true) based on a result. This works fine on strings using the Replace method, but that doesn't exist for boolean values. Can anyone tell me how to do this?
$bv is an array of objects, as follows.
ServerName,Domain,Environment,Tier0
ServerA,usa,dev,$false
ServerB,usa,sit,$false
I am trying to compare that list to another list of Tier0 computers ($t0List) that looks like this.
ServerB
ServerC
ServerD
So if there is a match between the ServerName in column 1 of $bv and an entry in $t0List, then I want to change the Tier0 column in $bv to $true.
foreach ($b in $bv) {
if ($t0List -contains $b.ServerName) {
$b.Tier0.Replace($b.Tier0,$true)
}
}
The error I get with the above code is...
Method invocation failed because [System.Boolean] does not contain a method named 'Replace'.

There's no need to use something like replace, just assign the value with =:
foreach ($b in $bv) {
if ($t0List -contains $b.ServerName) {
$b.Tier0 = $true
}
}
Want to simplify it even more? Just assign it the result of your -contains:
foreach ($b in $bv) {
$b.Tier0 = $t0List -contains $b.ServerName
}

Maybe this helps you:
You can flip between $true and $false just by telling it -not to be
-not $true brings back $false
-not $false
brings back
$true
So
foreach ($b in $bv) {
if ($t0List -contains $b.ServerName) {
$b.Tier0 = -not ($b.Tier0)
}
}
or
foreach ($b in $bv) {
if ($t0List -contains $b.ServerName) {
$b.Tier0 = $true
}
}
should work

Related

how to call a variable from a function

function test{
for($a=0;$a -le 2;$a++){
if($a -eq 1){break}
}
}
#----outside----
write-output $a
How to call a variable from a function and not to use return to get $a ?
Aside from using scopes as described in Rob's answer, there is also the possibility to send a parameter by reference.
This means that inside the function, a reference to the original variable is used, so whatever the function does with it, the original value is affected.
The downside is that when using references, you must use the Value property of the System.Management.Automation.PSReference type to access/alter your data as in the example below:
function test ([ref]$a) {
for ($a.Value = 0; $a.Value -le 2; $a.Value++){
if ($a.Value -eq 1) {break}
}
}
#----outside----
$a = 0
test ([ref]$a) # the parameter needs to be wrapped inside brackets and prefixed by [ref]
Write-Output $a
When sending a parameter that is an object type, like for instance a Hashtable, then by default it is always passed to the function by reference and for those you don't use the [ref] accellerator.
Have you tried to set the variable global?
$var="Test"
function test()
{
$global:var="blub"
}
test
$var
or
$a = 0
function test{
for($global:a=0;$global:a -le 2;$global:a++){
if($global:a -eq 1){break}
}
}
test
write-output $a

How to handle some function with if statement using Powershell?

I want to handle my function with if statement. I tried this code, but it always return me the value of $End_F which is "BB" even my file contains of "#AB#CD" . Anyone can help, please.
The file that I look for "#AB#CD" is like this.
Config
; Date="2019/06/12" Time="10:25:02" UTC="0"
;
Number
123456#AB#CD
$Get_SKU = Get-Content '.\Number.txt' | Where-Object {$_.Contains("#AB#CD")}
$Get_SKU
if($Get_SKU)
{$ML = "1"
AUTO_SELECT
}
else
{
END_Proc
}
Function AUTO_SELECT
{
$AT = "AA"
$AT
}
Function END_Proc
{
$End_F = "BB"
$End_F
}
$FE_UB = "4"
if($ML = "1" -and $FE_UB -eq $true)
{
G_BEGIN
}
if($ML = "1" -and $FE_UB -eq $false)
{
G_END
}
else
{
END_Proc
}
Function G_BEGIN
{
$begin = "Ready"
$begin
}
Function G_END
{
$ending = "Stop"
$ending
}
Some things need to be corrected to make your code work as expected.
Function AUTO_SELECT
{
$AT = "AA"
$AT
}
Function END_Proc
{
$End_F = "BB"
$End_F
}
Function G_BEGIN
{
$begin = "Ready"
$begin
}
Function G_END
{
$ending = "Stop"
$ending
}
$Get_SKU = Get-Content '.\Number.txt' | Where-Object {$_.Contains("#AB#CD")}
$Get_SKU
if($Get_SKU)
{
$ML = "1"
AUTO_SELECT
}
else
{
END_Proc
}
$FE_UB = "4"
if($ML -eq "1" -and $FE_UB)
{
G_BEGIN
}
if($ML -eq "1" -and !$FE_UB)
{
G_END
}
else
{
END_Proc
}
Explanation of Changes:
$Get_SKU will store either $null or a string depending on whether the Where-Object condition finds a match. As a result, I swapped out if ($Get_SKU -eq $true) in favor of if ($Get_SKU). This change will result in a $true evaluation if $Get_SKU is not $null.
I moved the functions to the top of the script because PowerShell executes the code starting from top to bottom. It is not compiled first. So you can't make a function call BEFORE the function has been read into memory and defined.
if ($ML = "1" -and $FE_UB -eq $true) has been updated to if ($ML -eq "1" -and $FE_UB) because variable assignment variable = value should not happen in an if statement condition. If you are comparing values, the proper operator here is -eq. Regarding $FE_UB, the same explanation applies as in the $Get_SKU changes.
$FE_UB -eq $false was changed to !$FE_UB. The removal of the -eq $false operator is based on the explanation given for $Get_SKU. The ! character is used to effectively -not the result. This will turn the value into a boolean value and then output the opposite boolean response. For example, !"string data" will output $False. !$null will output $True. I hope this part is clear.
Further Insight:
$True and $False evaluations
You can make just about anything return a boolean value. Three such ways include using casting, the -as operator, and !. There are many other ways and hacks to do this.
Casting:
$get_sku = "data"
[boolean]$get_sku
True
-as Operator:
$get_sku = $null
$get_sku -as [boolean]
False
Fun with !:
$get_sku = 4
!$get_sku
False
!!$get_sku
True

How can I sort Active Directory users into mutually exclusive groups using PowerShell?

I am trying to sort all Active Directory users into one of eight groups, but I cannot find an efficient and clean way of building my functions. In practice, I've already got something that works, so my question is just for my own education. This is my first time asking a question here, so I apologize if there's too little or too much information.
Basically, I've got to do some Active Directory logic to sort each user in my environment into one of eight groups.
Every user should be in one (and only one) of the eight groups.
Every sorting step just puts users in one category or the other.
There are three branches.
In reality, I am trying to entitle users to a VDI that matches their responsibilities. For now, I'll pretend I'm sorting RPG criminals into equipment groups or something.
The logic is something like this:
The criminals are all members of either the Seven Knives or Broken Gate organizations.
The members of those organizations are all either Rogues or Fighters.
The Rogues are all either Thieves or Assassins.
The Fighters are all either Brutes or Champions.
Depending on organization, class, and subclass, each criminal will have different equipment.
The end result is that users will be put into the appropriate group for their path down the tree - for example, CRIM_SK_ROG_THF, CRIM_BG_FTR_BRT, etc. I don't have any control over this, I just have to sort them out. I'm also not an actual programmer; I'm just a callow sysadmin that took 101-and 102-level college programming courses.
The problem I'm having is that I just can't seem to simplify my functions to one responsibility. PowerShell lets me write more than one output, so my first pass was to just have one function that returns two lists. It works fine, but it feels kludgy and vaguely disappointing.
Function filter_organization ($criminals) {
$seven_knives = New-Object System.Collections.ArrayList;
$broken_gate = New-Object System.Collections.ArrayList;
forEach ($criminal in $criminals) {
if ($criminal.MemberOf -contains "CN=Seven Knives,OU=Classes,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm") {
[void]$seven_knives.Add($criminal);
}
else {
[void]$broken_gate.Add($criminal);
}
}
Write-Output $seven_knives,$broken_gate;
}
$seven_knives, $broken_gate = filter_organization $all_criminals;
I've considered using global variables, but I'm not knowledgeable enough to decide where the taboo against them does or doesn't apply.
Function filter_class ($seven_knives) {
forEach ($criminal in $seven_knives) {
if ($criminal.MemberOf -contains "CN=Fighters,OU=Classes,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm") {
$global:seven_knives_fighters.Add($criminal);
}
else {
$global:seven_knives_rogues.Add($criminal);
}
}
}
I've also considered reference variables as parameters, but that both seems a lot like global variables and increases the parameters to three:
$seven_knives_rogues_thieves = New-Object System.Collections.ArrayList;
$seven_knives_rogues_assassins = New-Object System.Collections.ArrayList;
Function filter_subclass ($rogues, [ref]$thieves, [ref]$assassins) {
forEach ($rogue in $rogues) {
if ($rogue.MemberOf -contains "CN=Thieves,OU=Organizations,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm") {
$thieves.Value.Add($rogue);
}
else {
$assassins.Value.Add($rogue);
}
}
}
filter_subclass $seven_knives_rogues ([ref]$seven_knives_rogues_thieves) ([ref]$seven_knives_rogues_assassins);
But I don't particularly want to process the entire list twice for each split:
$criminals = Get-ADUser -Properties * -Filter *;
$seven_knives = filter_seven_knives $criminals;
$broken_gate = filter_broken_gate $criminals;
$seven_knives_fighters = filter_fighters $seven_knives;
$seven_knives_rogues = filter_rogues $seven_knives
So: how can I sort these users into eight mutually exclusive groups without violating best practices, single responsibility, or DRY? Should I forego functions and just use a ForEach loop to run every user through eight If conditions? Is there another, more advanced concept that I will need to learn?
Not sure why I wrote this, but here we are. Also, I didn't test this because I don't have your AD structure or your criminals.
function Add-UserToGroup {
[CmdletBinding(SupportsShouldProcess = $true)]
Param (
[Parameter(ValueFromPipeline = $true)]
[Microsoft.ActiveDirectory.Management.ADUser]
$Identity
)
try {
# Use this to create the group name
$StringList = New-Object -TypeName System.Collections.Generic.List[System.String]
$StringList.Add('CRIM')
# Get the membership if it doesn't exist
if ($null -eq $Identity.MemberOf) {
Get-ADUser -Identity $Identity -Properties MemberOf
}
# Process SevenKnives/BrokenGate
$SevenKnives = $Identity.MemberOf -contains "CN=Seven Knives,OU=Classes,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm"
$BrokenGate = $Identity.MemberOf -contains "CN=Broken Gate,OU=Classes,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm"
if ($SevenKnives -and $BrokenGate) {
throw "Criminal '$Identity' is a member of both Seven Knives and Broken Gate"
}
elseif ($SevenKnives) {
$StringList.Add('SK')
}
elseif ($BrokenGate) {
$StringList.Add('BG')
}
# Process Rogues/Fighters
$Rogues = $Identity.MemberOf -contains "CN=Rogues,OU=Classes,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm"
$Fighters = $Identity.MemberOf -contains "CN=Fighters,OU=Classes,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm"
if ($Rogues -and $Fighters) {
throw "Criminal '$Identity' is both a rogue and a fighter"
}
elseif ($Rogues) {
$StringList.Add('ROG')
# Process Theives/Assassins
$Thieves = $Identity.MemberOf -contains "CN=Thieves,OU=Organizations,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm"
$Assassins = $Identity.MemberOf -contains "CN=Assassins,OU=Organizations,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm"
if ($Thieves -and $Assassins) {
throw "Criminial '$Identity' is both a Thieve and an Assassian"
}
elseif ($Thieves) {
$StringList.Add('THF')
}
elseif ($Assassins) {
$StringList.Add('Ass')
}
}
elseif ($Fighters) {
$StringList.Add('FTR')
# Process Brutes/Champions
$Brutes = $Identity.MemberOf -contains "CN=Brutes,OU=Organizations,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm"
$Champions = $Identity.MemberOf -contains "CN=Champions,OU=Organizations,OU=Groups,OU=Accounts,DC=Quodeth,DC=Thule,DC=dm"
if ($Brutes -and $Champions) {
throw "Criminial '$Identity' is both a Brute and an Champion"
}
elseif ($Brutes) {
$StringList.Add('BRT')
}
elseif ($Champions) {
$StringList.Add('CHP')
}
}
$GroupName = $StringList -join '_'
if ($PSCmdlet.ShouldProcess($Identity,"Adding to group $GroupName")) {
Add-ADGroupMember -Identity $GroupName -Members $Identity
}
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
}
Get-ADUser -Filter * -Properties MemberOf | Add-UserToGroup -WhatIf

PowerShell adds other values to return value of function

It seems that PowerShell adds an additional variable to the return value of a function.
The function subfoo2 itself delivers the correct values, but as soon as PowerShell jumps back to the postion where I called the function (in foo1), value contains the value of an other variable ($msg)
(Have a look at the comments in the code)
writeMessageLog($msg){
...
Add-Content $msg
...
}
subfoo2{
writeMessageLog($msg)
return $UserArrayWithValues #During Debug, $Array is fine (1)
}
foo1{
$var = subfoo2 $UserArray # $var has now the value of $msg and $UserArrayWithValues (2)
#do something with var
}
Realcode:
function WriteLog
{
param ( [string] $severity , $msgNumber, [string] $msg )
...
$msgOut = $date + ... + $msg
Add-Content $msgout ( $msgOut )
...
}
function getFeatures
{
writelog 'I' 1002 $true $true "Load Features"
$Features = importCsv -pPath $FeatureDefintionFilePath
Writelog 'I' 1000 $true $true "Features Loaded"
return $Features # $Features has value as expected (1)
}
function GetUserFeatures ($pUserObject)
{
$SfBFeatures = ""
$SfBFeatures = getFeatures #SfBFeaures has Value of $msg and $Features (2)
...
}
Do I use the functions/return values wrong? What could lead to such behavior? Is it an issue if i call a function within a function?
If I remove $msgOut = $date + ... + $msg in writeMessageLog, the values are fine.
I'm pretty lost right now, and have no ideas where this comes from. Any ideas welcome.
This is how powershell works, basically everything that you print out will be returned as the function output. So don't output extra stuff. To force something to not output stuff you can do:
$null = some-command_that_outputs_unwanted_things
since everybody is obsessed with Out-Null I'll add this link showing several other ways to do that.
Within a function, everything you don't assign or pipe to a consuming cmdlet will get put to the pipeline and returned from the function - even if you don't explicit return it. In fact the return keyword doesn't do anything in PowerShell so the following is equivalent:
function Test-Func
{
"Hello World"
}
function Test-Func
{
return "Hello World"
}
So it looks like your writeMessageLog puts anything on the pipeline thus you have to either assign the value to anything:
$notUsed = writeMessageLog($msg)
or (prefered) pipe it to the Out-Null cmdlet:
writeMessageLog($msg) | Out-Null

Create a function with optional call variables

Is there a way to create a parameter in a PowerShell function where you have to call it in order to have it considered?
An example given by commandlet (the bold being what I want to do):
Invoke-Command -computername Server01 -Scriptblock {...}
Here is an example of what I want to do with the function
Function DoStuff($computername, -arg2, -domain $domain)
Test-parameter(-domain) if (-domain -eq $true) {
use $domain
}
Else {
$domain = "Domain1"
}
test-parameter($arg2) {
if ($arg2 -eq $true) {
Do something
}
else {
Do the opposite
}
}
So in summary:
If "-arg2" is present, I want something to happen in the script. If "-Domain" is present and has an argument with it, I want that to be used rather then the set argument.
Powershell provides a lot of built-in support for common parameter scenarios, including mandatory parameters, optional parameters, "switch" (aka flag) parameters, and "parameter sets."
By default, all parameters are optional. The most basic approach is to simply check each one for $null, then implement whatever logic you want from there. This is basically what you have already shown in your sample code.
If you want to learn about all of the special support that Powershell can give you, check out these links:
about_Functions
about_Functions_Advanced
about_Functions_Advanced_Parameters
I don't think your question is very clear, this code assumes that if you're going to include the -domain parameter, it's always 'named' (i.e. dostuff computername arg2 -domain domain); this also makes the computername parameter mandatory.
Function DoStuff(){
param(
[Parameter(Mandatory=$true)][string]$computername,
[Parameter(Mandatory=$false)][string]$arg2,
[Parameter(Mandatory=$false)][string]$domain
)
if(!($domain)){
$domain = 'domain1'
}
write-host $domain
if($arg2){
write-host "arg2 present... executing script block"
}
else{
write-host "arg2 missing... exiting or whatever"
}
}
Not sure I understand the question correctly.
From what I gather, you want to be able to assign a value to Domain if it is null and also what to check if $args2 is supplied and according to the value, execute a certain code?
I changed the code to reassemble the assumptions made above.
Function DoStuff($computername, $arg2, $domain)
{
if($domain -ne $null)
{
$domain = "Domain1"
}
if($arg2 -eq $null)
{
}
else
{
}
}
DoStuff -computername "Test" -arg2 "" -domain "Domain2"
DoStuff -computername "Test" -arg2 "Test" -domain ""
DoStuff -computername "Test" -domain "Domain2"
DoStuff -computername "Test" -arg2 "Domain2"
Did that help?