Powershell function parameters with multiple words/values - powershell

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

Related

Uploading a Terms of Use PDF via Powershell to Azure using Graph

I've just started trying to use the MgGraph module to try and perform operations on an Office 365 tenant to perform some configuration automation and I'm having a hard time wrapping my head around some of these cmdlets - in particular, the one I'm currently messing around with, New-MgAgreement, in an attempt to automatically upload a terms of use file.
I guess, for starters, is this a viable path to achieving this? Is there some other method I should attempt to be using to try and accomplish this? And if not, I guess how do I go about actually doing this? In short, this is what I've attempted:
I'm utilizing a file picker via Windows Forms to prompt to capture the file I wish to upload
I've created a hash for the File parameter of the cmdlet to satisfy the properties needed (I thought...) as well as filling out the other parameters needed to provide information about the terms of use object being created on the tenant
When I try to run the command, I was initially told that "value cannot be null - parameter name source" which makes sense because I didn't see any parameters specifying source in the documentation for the physical file, so I tried simply adding a "source" parameter to the New-MgAgreement cmdlet thinking I just missed it in the documentation, but upon trying to do that, it tells me "a parameter cannot be found that matches parameter name 'source'.
Just to give an idea of my mindset when trying to achieve this, this is a code snippet hopefully illustrating the direction I'm trying to go about doing this:
Function Main {
Connect-Modules
Set-Tenant-Terms-Of-Use
Get-Tenant-Terms-Of-Use
}
Function Connect-Modules {
Connect-AzureAD
$tenantId = Get-AzureADTenantDetail | Select-Object ObjectId
C:\Windows\System32\cmd.exe /c start shell:AppsFolder\Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge -private https://microsoft.com/devicelogin
Connect-MgGraph -Scopes "User.Read.All", "Group.ReadWrite.All", "Agreement.Read.All", "Agreement.ReadWrite.All" -TenantId $tenantId.ObjectId.ToString()
}
Function Set-Tenant-Terms-Of-Use {
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Filter = 'PDF (*.pdf)|*.pdf'
}
$companyName = Read-Host "Please enter your company name: "
$null = $FileBrowser.ShowDialog()
$fileHash = #{
DisplayName = "All users terms of use";
FileName = $FileBrowser.SafeFileName
IsDefault = $true
IsMajorVersion = $true
Language = "English (Default)"
}
New-MgAgreement -DisplayName "$companyName Terms of Use" -File $fileHash -source $FileBrowser.FileName
}
Function Get-Tenant-Terms-Of-Use {
$termsOfUse = Get-MgAgreement | Select-Object Id, DisplayName
Write-Host $termsOfUse.Id.ToString()
Write-Host $termsOfUse.DisplayName.ToString()
}
Main
Disconnect-MgGraph
I'll admit... I'm kind of completely lost on this one - I haven't really worked with Graph previously and I'm trying to get the hang of it since it seems to have a lot of capabilities that hook into areas of an Azure/O365 tenant that the other modules I'm used to using do not. I appreciate any schooling I can get on the topic! Thank you!
EDIT - So I can get this to work the way I want via Graph Explorer... I realized I needed to convert my file to base64 and put the value under the data property under the FileData parameter from within the File Parameter... but when I try to mirror this in PowerShell using the same parameter values as what is included in Graph Explorer I just get a "New-MgAgreement: Value cannot be null. Parameter name: source"... so it's just that I don't know how to format this using the cmdlet... and I just can't wrap my head around how to use the complex parameters apparently given the documentation.
I was able to reproduce this issue. As an alternative, you can use Invoke-MgGraphRequest passing in the agreements endpoint and upload the agreement - this worked for me see below:
Agreement Resource Sample Data
$data
{
"displayName": "MSGraph Sample",
"isViewingBeforeAcceptanceRequired": true,
"files": [
{
"fileName": "TOU.pdf",
"language": "en",
"isDefault": true,
"fileData": {
"data": "SGVsbG8gd29ybGQ="
}
}
]
}
#Using IGR
Invoke-MgGraphRequest -Uri https://graph.microsoft.com/v1.0/agreements -Method POST -Body $data
Please let me know if this helps and if you have further questions.

Joining strings and # character missing

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.

How to accept either a "live" object or a deserialized object of the same type in a param block? [duplicate]

This question already has an answer here:
Can a PowerShell function handle multiple input types?
(1 answer)
Closed 1 year ago.
I have a script that deals with Active Directory User objects (Microsoft.ActiveDirectory.Management.ADUser). I explicitly list the type in the function that processes these objects:
function Write-ADUser {
param (
[Microsoft.ActiveDirectory.Management.ADUser]$user
)
(...)
I also want this function to be able to take objects from remote sessions. The challenge is that objects returned from remote sessions are of the deserialized variety:
C:\> icm -session $sess { get-aduser -identity testuser -credential $cred } | gm
TypeName: Deserialized.Microsoft.ActiveDirectory.Management.ADUser
Is there a way to have my function param block accept either the "live" object or the deserialized variant? My function doesn't need to use methods - the deserialized variant has (or can be made to have) what I need.
The parameter sets idea was interesting and a helpful lead. After reviewing the documentation, this is the best option I could come up with:
function Write-ADUser {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[ValidateScript({
if ($_.GetType().Name -notin #('ADUser', 'PSObject')) {
throw ("Error: Invalid type ````{0}'' - expecting ADUser.") -f $_.GetType().Name
} else {
$true
}
})]
$user
)
...
One other comment. When looking into parameter sets I kept getting an error about ADUser. However, upon further digging I believe that error is because the Microsoft Active Directory PowerShell module isn't installed on my test computer. Therefore, the 'ADUser' type isn't defined. Because I want this script to run on computers that don't necessarily have the ADModule I am using the above logic. However, if I could guarantee that ADModule was present then I think parameter sets would be the way to go.
Apologies for not providing clearer requirements. I'm still learning PowerShell...
Note - updated based on feedback from #zett42

Use Connect-PnPOnline in a workflow

Good morning, everyone,
I have a problem in retrieving information from SharePoint groups. I do it in a workflow, first I get the list of all groups and then I use it in a foreach-parallel to list the members of these groups.
The problem is that the connection doesn't seem to be maintaining and my query doesn't recover all the groups.
Here is a piece of the code:
workflow GetGroup {
param(
[Parameter(Mandatory)]
[String]$SPOSite,
[System.Management.Automation.PSCredential]$SPOCreds
)
$KeepAlive = Connect-PnPOnline -Url $SPOSite -Credentials $SPOCreds -ReturnConnection
$GetGroups = Get-PnPGroup
ForEach -Parallel -ThrottleLimit 512 ($Group in $GetGroups)
{
$GroupName = $Group.LoginName
$Users = Get-PnPGroupMembers -Identity $groupName -Connection $workFlow:KeepAlive
}
}
GetGroup -SPOSite "https://xxx.sharepoint.com/sites/xx -SPOCreds (Get-Credential)
The expected result would be an array initialized at the beginning of the workflow with a PSCustomObject object that is added to our array. This table is made up of 3 things: The name of the group, the names of the people in that group, the emails of the people.
Unfortunately the table is only partially generated because a workflow here is the error I find nothing on the subject:
Impossible to link the "Connection" parameter. Impossible to convert the value "SharePointPnP.PowerShell.Commands.Base.SPOnlineConnection" of the type "SharePointPnP.PowerShell.Commands.
Deserialized.SharePointPnPnP.PowerShell.Commands.Base.SPOnlineConnection"
Thank you for your help.
I ran into the same error in a similar scenario, and this article helped me. Workflow is converting the Connection object ($KeepAlive in your case) to a deserialized format, and so the other cmdlet doesn't accept it. You will just have to wrap those cmdlets with InlineScript, or use a PowerShell script runbook instead of a PowerShell workflow runbook.

Display PowerShell mandatory parameter options?

Is there a way to have PowerShell display the options possible for a parameter? I have the following Parameter which requires one of the resource groups in Azure to be selected.
[CmdletBinding()]
Param(
[Parameter(Mandatory = $true, HelpMessage = "Enter the name of the resource group you would like to use.")]
[ValidateScript( {$_ -in (Get-AzureRMResourceGroup | Select-Object -ExpandProperty ResourceGroupName)})]
[String]$ResourceGroup
)
ValidateScript will check to see if it is one of the Resource groups in Azure, but my question is how can I display a list of the resource groups so that the person running the script knows what possible options they can input for the parameter? Can I use Write-Host or something within the Param block?
Something like this would be great to display on the line above where they input the value for the parametera (but not static options I want the script to query azure and display the list of resource groups the user can choose):
Please choose one of the following resource Groups: RG1 RG2 RG3 RG4
Thank you.
As PetSerAI mentioned the answer is to use tab completion.