Joining strings and # character missing - powershell

I'm assuming its obvious, but I'm completely missing it. I've got a PowerShell script I'm working on to automate user creation. One of my functions generates the email address. Right now, its not doing anything fancy, just joining the username and domain. I'll be adding more later to increment the value if in use. But I can't get the basic version to work.
function generateMailbox {
param(
[parameter(Mandatory=$true)]
$userName,
[parameter(Mandatory=$true)]
$domain
)
$uniqueMailbox = $null
#set initial value for $uniqueMailbox as the initial mailbox name
$uniqueMailbox = "$userName#$domain"
return $uniqueMailbox
}
So, pretty straightforward. BUT... the "#" is missing from the emails it generates. I put in a break point on the return and verified $uniquemailbox is missing that "#". So if $userName = "uname" and $domain = "domain.com", then $uniqueMailbox will be set to "unamedomain.com"
While in the debug mode, I can manually enter $uniqueMailbox = "$userName#$domain" and then the value returns correctly. For the life of me I can't see my mistake.
I did double check the $uniqueMailbox variable isn't used anywhere outside this function.

Related

Why is my powershell Base64 String truncated to 36 characters?

I'm using PowerShell inside VSTS Build for querying the VSTS API. I'm using PAT for authentication.
However, I see it failing when I generate the Auth string. Here is my code inside
$VstsAccessEmail = $Env:VstsAccessEmail
$VstsAccessToken = $Env:VstsAccessToken
$pair = "${VstsAccessEmail}:${VstsAccessToken}"
$base64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($pair))
Write-Host $base64
If I see the outcome here, I see the first 36 characters of the actual auth string. I generated the string using Powershell on my machine and I get the entire 108 characters. I have hard-coded this for time being to test this like the following steps that follow the above code.
$base641 = "a2FuZ2thbi5nb3N3YW1pQHVuaXN5cy5jb206cHh4b25oaHBlNmtjb3g3aTRhdHZxMzdoNms2ZnpuNHhyaWhyZ2ozdGZ3ejRlNmxxxxXXXX=="
if($base64.length -ne 108){
$base64 = $base641
}
Write-Host "base64 is: $base64 "
This works correctly. Initially, I thought it might be an issue with Writing to host. However, If I do invoke the RestMethod without updating with the hard-coded one, I get 401 Unauthorized.
Please help.
UPDATE:
I found the issue. I set the VstsAccessToken as a secret in the build variables. So the value is not coming through. making it unsecured works fine.
Can someone help how this can be done keeping the token as a secret?
SOLVED
Using $Env:variable does not allow to use the value when the variable is secret. However, passing it as a parameter to the PowerShell let the code read it, though the output to log is masked.
This section is definitely wrong. I see one thing that's definitely wrong, another that's possibly wrong.
$VstsAccessEmail = $Env:VstsAccessEmail
$VstsAccessToken = $Env:VstsAccessToken
$pair = "${VstsAccessEmail}:${VstsAccessToken}"
$base64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($pair))
$pair = "${VstsAccessEmail}:${VstsAccessToken}" should be $pair = ":${VstsAccessToken}". No email address is neccessary, just a colon and then the auth token.
UTF8.GetBytes may be wrong. I always use ASCII.GetBytes.
Using $Env:variable does not allow to use the value when the variable is secret. However, passing it as a parameter to the PowerShell let the code read it, though the output to log is masked.

Is there a way to make a Powershell function ignore a default parameter's value if its parameter set is not in use?

I understand from this answer that when you have default parameters and multiple parameter sets in a Powershell function, the default parameter values will be instantiated even if the parameter set in use is not the one in which they are inserted.
Is there a way to avoid this?
For example, in the function below, assuming that there is a really expensive calculation used to compute the default value of $FirstParameter, I would like to avoid using it when it is not necessary:
function PrintStuff {
[CmdletBinding(DefaultParameterSetName='FirstSet')]
Param(
[Parameter(ParameterSetName='FirstSet')]
[String]$FirstParameter=(ReallyExpensiveFunction),
[Parameter(ParameterSetName='SecondSet')]
[String]$SecondParameter
)
if (-not ($FirstParameter -eq $null)) {Write-Host $FirstParameter}
Write-Host "$($PSCmdlet.ParameterSetName)"
}
function ReallyExpensiveFunction {
# Very expensive calculation
"I Am First"
}
However, at the moment running it would still give me the results below:
PS C:\> PrintStuff
# I Am First
# FirstSet
PS C:\> PrintStuff -SecondParameter "don't print this"
# I Am First
# SecondSet
As per above, when SecondSet is used $FirstParameter is still being defined. Is there a way to get only SecondSet printed when the second parameter set is used?
Bear in mind, I am looking to find out if there is a solution which would allow me to keep ReallyExpensiveFunction as the default value for $FirstParameter, and avoid solutions which would involve transferring the logic to the body of the function, such as:
...
Param(
[Parameter(ParameterSetName='FirstSet')]
[String]$FirstParameter,
...
)
if ($PSCmdlet.ParameterSetName -eq 'FirstSet' -and ($FirstParameter -eq '')) {
$FirstParameter = ReallyExpensiveFunction
}
...
Sorry if the pitch is too specific, but I am curious to find out if this is possible.
Unfortunately, the answer is no. ParameterSet allows to present a simpler interface to user for complex argument sets by filtering out the non-relevant ones. However, PowerShell goes through each parameter, whether it is in the selected parameterset or not and assign the default value to the parameter, if you specify one. So, simply put in the context of your question, ParameterSet may be thought as just a filter for presentation.

Powershell Parameter passing issue

I have a strange one I have searched the existing Q&A and haven't found a match.
I have written my functions using parameter validation using the basic format
function FunctioName
{
[CmdletBinding()]
Param(
[parameter(Mandatory)]
[String]$VariableName
)
When I set the parameter to Mandatory as above I get a parameter binding exception indicating a null value was passed. Running the script in debug I can see the function parameter being passed is not null and is a valid string.
When I run the script in the exact same way without the mandatory flag the string is passed into the function and it executes correctly.
Has anyone got any ideas, what could be the issue. This problem is affecting a number of functions in my application interestingly it appears that the affected functions all have only a single parameter functions with multiple parameters do not appear to be affected.
Ok thanks guys for your feedback its much appreciated. BTW i am using powershell 5 .
Further to the issue, looking into it further I found that the variable was being passed to the function as an array of strings, however an empty string value was being appended into the array which I believe was the cause for the issue. This is where it starts to get interesting, I will need to give a bit more background.
The script I am running queries active directory for user attributes meeting specific conditions, those that match I create an array of strings with each value a delimited value of the user,hostname and other attribute properties.
To ensure that I am getting the latest values I use the ASDI GetInfo method,which seems to trigger the odd behavior.
At a high level the functions are
Function GetuserAttr
{
$inscopeusers = New-Object System.Collections.ArrayList
$accountlist = (Get-ADUser -Filter { attribute1 -eq "value"} -Properties attribute1).SamAccountName
foreach ($user in $accountlist)
{
$DN = getDN($user) # basically a funtion I wrote to create ASDI object for user account.
$DN.GetInfo() # this method call appears to cause issues
$attr1 = $DN.Get("Attribute1")
$attr2 = $DN.Get("Attribute2")
$hoststring = "$($user)|$($attr1)|$($attr2)"
$inscopeusers.Add($hoststring) > null
}
return $inscopeusers
}
The string array returned in this function is fed into a number of other functions, one of which is the one that was giving the error that I originally brought up.
The thing is when I use the GetInfo method the array returned by this function contains several null values in the array, when I remove the command the array has no null strings.
Even more strange when I am operating on the array in other functions it appears that the array looses some of its properties when the GetInfo method is used. So for instance I am able to use the foreach loop to iterate through array values but I cannot access an array value by index such as $array[1].
By simply commenting out the GetInfo method call in the function the array returned seems to function normally and you can access array values by index.
I have another function that also uses GetInfo and returns a hash table, when I try to operate on the returned hashtable I cannot access values using a key value such as $hashtable['key'], but I can access them using $hashtable.key. I know this is really weird and can't really think what it could be
Has any one else experienced a similar problem.
You're missing an argument.
Function Test
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[String]
$Variable
)
Write "$Variable"
}

Powershell parameter handling

I have a powershell script that looks something like this.
Function Write-DeploymentConf {
param
(
[Parameter(Mandatory)]
[ValidateLength(4, 15)]
[String]
$CustomerName,
[Parameter(Mandatory)]
[ValidateLength(4, 15)]
[String]
$FacilityName = $CustomerName,
[Parameter(Mandatory)]
[ValidateLength(5, 1024)]
[String]
$LocationAddress,
[Parameter(Mandatory)]
[ValidateSet("onpremise", "cloud")]
[String]
$DeploymentMode = "onpremise"
)
Process {
$( Write-Host "CustomerName: $CustomerName")
$( Write-Host "FacilityName: $FacilityName")
$( Write-Host "LocationAddress: $LocationAddress")
$( Write-Host "DeploymentMode: $DeploymentMode")
}
}
Write-DeploymentConf
It's close to what I want, but there are a few things wrong with it.
It silently accepts inputs that will fail validation, then holds them until the user has entered values for every parameter, then dies with an error message. I don't want to die, I just want to re-prompt when validation fails. And, ideally, I want to re-prompt right away, not after everything has been input. I only want to re-prompt for ones that failed validation.
I want to tell the user what the validation rules are before they mess it up. If the only allowable values are "onpremise" or "cloud", then the user should be told that. Ideally the user should just type "1, enter" for "onpremise", "2, enter" for "cloud".
Some parameters have a reasonable default that I want to prompt the user to accept. For example, if $CustomerName is "abcd", then "abcd" is a reasonable default value for $FacilityName. When prompting $FacilityName, I want to allow the user to change it, or press "enter" to accept "abcd" as the default. Not only do I want to accept that as a default, but I want to show that the 'abcd' will be the default value on the prompt line. Likewise, "onpremise" is a reasonable default for $DeploymentMode, but I want them to affirmatively accept that default.
Some sets of parameters are only conditionally mandatory. E.g., if this is a cloud deploy, then a bunch of credentials become mandatory to supply. Can I keep the nice "accept from command line but fall back to prompting" behavior with runtime conditionals?

Powershell function parameters with multiple words/values

I'm new to powershell and my first module is for simply adding users to the local admin group on remote computers. It looks like:
function AddAdmin {
[CmdletBinding()]
Param(
[Parameter (Mandatory=$True,ValueFromPipeline=$True,Position=1) ]
[string[]]$Computer,
[Parameter (Mandatory=$True,ValueFromPipeline=$True,Position=2) ]
[string]$username
)
$Domain = "the domain"
$Group = [ADSI]"WinNT://$Computer/Administrators,group"
$Usertoadd = [ADSI]"WinNT://$Domain/$username,user"
$Group.Add($Usertoadd.Path)
}
so I can just type addadmin computername username and it gets added. I want to do the same for groups, the problem I'm having is figuring out how to set a parameter that has multiple values/words. For example let's say I want to add a group called Executive Team to local admins. addadmin computername executive team doesn't work - it only picks up executive as the value.
Googled quite a bit and can't seem to figure this out, I'm sure I'm missing something simple.
You just have to put the multiple words value into double quotes :
addadmin computername "executive team"
Positions start at 0, just FYI, and while JPBlanc's answer is correct (and honestly better from a technical standpoint) you should be able to add this to your Parameter list for the User Name to get the same results without having to put them in quotes.
ValueFromRemainingArguments = $true