Use value directly in script - powershell

I have the following script. The issue i have is that i need to create a value before using the information gathered from the csv file. The first code is what works for me, the second code is how i would want to use it, where the value $uid is not create before
$users = import-csv c:\temp\users.csv
foreach ($user in $users){
$uid= $user.UserPrincipalName+":\calendar"
add-mailboxfolderpermission -identity $uid -user "calendar_reviewer" -AccessRights LimitedDetails
}
$users = import-csv c:\temp\users.csv
foreach ($user in $users){
add-mailboxfolderpermission -identity $user.UserPrincipalName+":\calendar" -user "calendar_reviewer" -AccessRights LimitedDetails
}

In short, your string composition isn't working like you're expecting it to.
If we use the following test function it'll demonstrate what's happening:
function Invoke-MyFunction
{
param( $Identity, $OtherParams )
write-host "identity = '$Identity'";
write-host "otherparams = '$OtherParams'";
}
In your first (working) example, PowerShell is concatenating the strings:
$uid = $user.UserPrincipalName+":\calendar"
Invoke-MyFunction -Identity $uid
# identity = 'myname:\calendar'
# otherparams = ''
but in your broken sample, it's not treating the $user.UserPrincipalName+":\calendar" as a single expression - it's treating +":\calendar" as a separate string that it passes as a second positional parameter:
Invoke-MyFunction -Identity $user.UserPrincipalName+":\calendar"
# identity = 'myname'
# otherparams = '+:\calendar'
There's a few different ways to get PowerShell to treat your parameter as an expression so it evaluates it before passing the value as a parameter:
Grouping Operator
As recommended by #zilog80 in the comments, wrap it in the Grouping operator (i.e. ( ... )) to force PowerShell to evaluate the expression before passing the result into the parameter:
Invoke-MyFunction -Identity ($user.UserPrincipalName+":\calendar")
# identity = 'myname:\calendar'
# otherparams = ''
String Interpolation
Per #Abraham Zinala's comment, use string interpolation (note the use of the Subexpression operator (i.e. $( ... ) to substitute in the value of $User.UserPrincipalName)
Invoke-MyFunction -Identity "$($User.UserPrincipalName):\calendar"
# identity = 'myname:\calendar'
# otherparams = ''
Assign to a variable
As you've already found, you can force the expression to be evaluated by assigning it to a temporary variable, then pass the variable as a parameter:
$uid = $user.UserPrincipalName+":\calendar"
Invoke-MyFunction -Identity $uid
# identity = 'myname:\calendar'
# otherparams = ''

Related

Using a Variable as a Parameter - Powershell

I am trying to pass a variable through to be used as a parameter within a function. I'm not even sure if this is possible but below is what i am attempting to accomplish, what i have tried so far keeps kicking out a "positional parameter cannot be found that accepts argument" error
$Var = Read-host "enter Attribute number"
$CustomAtt = "CustomAttribute$Var"
Get-Mailbox -Identity $Email | Set-Mailbox -$CustomAtt "TestTest"
You cannot set cmdlet arguments that way in powershell. You can do what your are attempting to do by using a feature called argument splatting. Simply store your arguments in an array or hashtable and then apply them to the cmdlet using # symbol.
Like this:
$mailBoxAttributes = #{
$CustomAtt = "TestTest" }
Get-Mailbox -Identity $Email | Set-Mailbox #mailBoxAttributes

Object properties and Variables

What I am working with is this (Exchange online powershell):
get-publicfolder -Identity "\TestFolder" -Recurse|Where{$_.mailenabled -eq "true"}
So what I am interested in is getting the parentpath and name properties from that.
How do I assign that to a variable so parentpath and name are on the same line
Right now if I do
$myvar = $MailEnabledFolder.parentpath,$MailEnabledFolder.name
Then the variable is built like:
\TestFolder
\TestFolder\Test3
\TestFolder\Test3
Info
Processing
QA
I want it to be
\TestFolder\Info
\TestFolder\Test3\Processing
\TestFolder\Test3\QA
Thank you
Your $MailEnabledFolder.parentpath and $MailEnabledFolder.name values are apparently arrays of values, so you must process them in pairs:
$array1 = $MailEnabledFolder.parentpath
$array2 = $MailEnabledFolder.name
foreach ($i in 0..($array1.Count-1)) {
Join-Path $array1[$i] $array2[$i]
}

Set-ADuser extensionAttribute won't work but things like title will

I am writing a simple script that takes an already created user and updates an attribute based on what the admin put in.
The code works just fine if I replace extensionAttribute with for example title or something like that, but it won't with extensionAttributes.
I have tried a few things and other extensionAttributes but the code is so simple and it works with other Attributes. I am guess extensionAttributes require a bit more in the code that I am missing.
$name = Read-Host "AD Logon Name"
$key = Read-Host "Azure Key"
Set-ADUser $name -extensionAttribute6 $key -PassThru
Set-ADUser : A parameter cannot be found that matches parameter name 'extensionAttribute6'
Even though it exists it is not finding it.
Set-ADUser has a limited set of parameters covering the most commonly used attributes in AD. However, given the sheer amount of existing attributes and the fact that the AD schema is extensible, an attempt to have all attributes represented as parameters just wouldn't be feasible.
For attributes that are not represented as parameters use the parameter -Add or -Replace with a hashtable argument.
Set-ADUser $name -Replace #{'extensionAttribute6' = $key} -PassThru
Old thread, but this worked for me:
Import-Csv -Path "C:\data\12345.csv" |ForEach-Object {
Set-ADUser $_.samAccountName -replace #{
description = "$($_.description)"
extensionAttribute1 = "$($_.extensionAttribute1)"
extensionAttribute3 = "$($_.extensionAttribute3)"
initials = "$($_.initials)";
#additionalAttributeName = "$($_.additionalAttributeName)"
#additionalAttributeName = "$($_.additionalAttributeName)"
#additionalAttributeName = "$($_.additionalAttributeName)"
#additionalAttributeName = "$($_.additionalAttributeName)"
#additionalAttributeName = "$($_.additionalAttributeName)"
}
}
The top row of your .csv file would look like the following for this example:
samAccountname,description,extensionAttribute1,extensionAttribute3,initials

How to Combine and split values by delimiter?

I have a script that has a switch, -Add, allowing the addition of 1 role and 1 member (into that role) in a database at a time.
PS> script1.ps1 -Add Database1 role1 member1
PS> script1.ps1 -Add Database1 role1 member2
PS> script1.ps1 -Add Database1 role2 memberx
PS> script1.ps1 -Add Database1 role3
PS> script1.ps1 -Add Database1 role4 membery
It wont be practical to have to run that script everytime if there are more than 1 member or 1 roles/members to add a time. hence, i'd like to update my script with bulk addition of roles/members at once, which would be helpful especially for a TFS use case, in which the user wont have to create multiple releases just to add a couple roles/members for the same database.
i am thinking of implementing a delimiter split, in which for every semicolon, that indicates the start of a new role, and any comma delimited members, all belong to one role unless a semicolon follows (not necessary if its the end of the input, unless it would be hard to achieve something like that with regex?)
pseudocode:
$RoleInput.Split(";") | ForEach {
$role = "$_";
$MemberInput.Split(",") | ForEach {
$member = "$_";
#-Add $DBName $role $member
}
}
ultimately, i would like to achieve similar to the following:
PS> script1.ps1 -Add Database1 role1;role2;role3;role4 member1,member2;memberx;;membery
this means that for role1, member1 and member2 would be added
for role2, memberx is added, for role3, no member is added, and for role 4, membery is added
how would i achieve this correctly with regex to account for whitespaces, and end of input?
Save roles and members in a CSV
"Role","Members"
"role1","member1;member2"
"role2",
"role3","memberX"
then load the CSV using Import-Csv, split the member list at the chosen secondary delimiter (semicolons in the example above), and call your script with those arguments.
Import-Csv 'input.csv' | ForEach-Object {
$role = $_.Role
if ($_.Members) {
$_.Members -split ';' | ForEach-Object { & script1.ps1 -Add $role $_ }
} else {
& script1.ps1 -Add $role
}
}
If you can modify the parameters of your script you could change them to something like this:
[CmdletBinding()]
Param(
[Parameter(ParameterSetName='add', Mandatory=$true)]
[Switch]$Add,
[Parameter(ParameterSetName='add', Mandatory=$true)]
[String]$Role,
[Parameter(ParameterSetName='add', Mandatory=$false)]
[String[]]$Members = #()
)
so that the script accepts a list of members via a named parameter (you also need to adjust how the members are processed in the script, of course), and then call it like this:
Import-Csv 'input.csv' | ForEach-Object {
$params = #{'Role' = $_.Role}
if ($_.Members) { $params['Member'] = $_.Members -split ';' }
& script.ps1 -Add #params
}
This second example uses splatting for passing the parameters to the script.

Load Variables from text file and if key exists in hashtable, use key value

I'm trying to create a script, where the variables are outside of the PowerShell script. It's just a text file. In that file, we define server name, group names and their values. Group names meaning that the host has these strings in their hostname.
Hostnames have the higher priority, and Group names have the least priority. If neither is present, then there is a default value (inside the ps script).
The following elseif condition isn't working for me and I've tried a lot of other ways but could not get it working.
elseif ($MachineName.Contains("$var.key")) { # Doesn't work. What can be used here?
$foldername = ("$var.value") # Doesn't work. What can be used here?
}
Content of vars.txt:
#Groups
DB=folder7
#Hostnames
server_JIRA_001=folder3
server_DB_001=folder5
server_DB_005=folder6
If a hostname is server_DB_006 it will use value "folder7" as it has the "DB" in their hostname, but if the hostname is server_DB_005 then it will use value "folder6"
Content of script.ps1
$MachineName = "server_DB_006"
$var = (Get-Content "c:\Users\user\Desktop\vars.txt" -Raw | ConvertFrom-StringData)
if ($var.$MachineName -ne $null) {
# if hostname exists, then use its var.value
$foldername = ($var.$MachineName) # returns value of $var.hostname
} elseif ($MachineName.Contains("$var.key")) { # Doesn't work. What can be used here?
# if no hostname defined, then search for group name and use its var.value
$foldername = ("$var.value") # Doesn't work. What can be used here?
} else {
foldername = "default_folder"
}
So you can create a function who will bring back the key name if a string contains it. It loops through the keys and checks the $Text to see if it contains the key text else returns false.
function StringContainsHashTableKey(){
PARAM(
[string]$Text,
[hashtable]$HashTable
)
foreach($i in $Hashtable.Keys.GetEnumerator()){
if($Text -like "*$i*" ){
return $i
}
}
return $false
}
$Var = Get-Content "c:\Users\user\Desktop\vars.txt" -Raw | ConvertFrom-StringData
$MachineName = "server_DB_006"
$KeyMatch = StringContainsHashTableKey -Text $MachineName -HashTable $Var
if ($var.$MachineName -ne $null) { #if hostname exists, then use its var.value
$foldername = ($var.$MachineName) # returns value of $var.hostname
}
# If no hostname defined, then search for group name and use its var.value
Elseif ($KeyMatch -ne $false) { # Doesn't work. What can be used here?
$foldername = $var.$KeyMatch
}
Else {
$foldername = "default_folder"
}
$foldername