Powershell and Sharepoint - Update list - powershell

I've been reading a ton of these articles that say to use Get-SPWeb, but I've never been able to get those functions working due to authentication errors. I have build my own little functions to do what I need but I'm struggling to figure out what I'm doing wrong for my update function. Below are the functions I've built, and all of them work:
If (!$cred) {$cred = get-credential -UserName "$ENV:Username#$ENV:UserDomain.com" -Message "Enter your office 365 login"}
function Get-AuthenticationCookie($context)
{
$sharePointUri = New-Object System.Uri($context.site.Url)
$authCookie = $context.Credentials.GetAuthenticationCookie($sharePointUri)
if ($? -eq $false) #https://ss64.com/ps/syntax-automatic-variables.html
{
return $null
}
$fedAuthString = $authCookie.TrimStart("SPOIDCRL=".ToCharArray())
$cookieContainer = New-Object System.Net.CookieContainer
$cookieContainer.Add($sharePointUri, (New-Object System.Net.Cookie("SPOIDCRL", $fedAuthString)))
return $cookieContainer
}
function Get-SharepointContext
{
Param(
[Parameter(Mandatory = $true)]
$siteUrl,
[Parameter(Mandatory = $false)]
$cred)
If (!$cred) {$cred = get-credential -UserName "$ENV:Username#$env:USERDNSDOMAIN" -Message "Login"}
[string]$username = $cred.UserName
$securePassword = $cred.Password
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.ClientContext")
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
$ctx.RequestTimeOut = 1000 * 60 * 10;
$ctx.AuthenticationMode = [Microsoft.SharePoint.Client.ClientAuthenticationMode]::Default
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePassword)
$ctx.Credentials = $credentials
Return $ctx
}
function Add-SharepointListEntry
{
#example
#Add-SharepointListEntry -cred $cred -clientName $DestinationPages
Param(
[Parameter(Mandatory = $true)]
$cred,
[Parameter(Mandatory = $true)]
$sitename,
$siteUrl = "https://$env:Userdomain.sharepoint.com/$sitename",
[Parameter(Mandatory = $true)]
$ListName,
$SharepointData
)
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
# Bind to site collection
$Context = Get-SharepointContext -siteUrl $siteUrl -cred $cred
# Get List
$List = $Context.Web.Lists.GetByTitle($ListName)
$Context.Load($List)
$Context.ExecuteQuery()
# Create Single List Item
$ListItemCreationInformation = New-Object Microsoft.SharePoint.Client.ListItemCreationInformation
$NewListItem = $List.AddItem($ListItemCreationInformation)
#construct the entry to insert
$NewListItem["Title"] = $SharepointData.Title #Client Name
$NewListItem["Description"] = $SharepointData.Title
#These objects should pass right through
$NewListItem["Client"] = $SharepointData.Client
$NewListItem["Author"] = $SharepointData.Author
$NewListItem["Initials"] = $SharepointData.Author
$NewListItem["Created"] = $SharepointData.Created
$NewListItem.Update()
$Context.ExecuteQuery()
}
Function Get-SharepointListData
{
#example
#Get-SharepointListData -cred $cred
Param(
[Parameter(Mandatory = $true)]
$cred,
[Parameter(Mandatory = $true)]
$sitename,
$siteUrl = "https://$env:Userdomain.sharepoint.com/$sitename",
[Parameter(Mandatory = $true)]
$ListName
)
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
# Bind to site collection
$Context = Get-SharepointContext -siteUrl $siteUrl -cred $cred
#Retrive the List
$List = $Context.web.Lists.GetByTitle($ListName)
#Get All List Items
#reference https://gallery.technet.microsoft.com/office/How-to-do-a-CAML-Query-6f5260cf
$Query = New-Object Microsoft.SharePoint.Client.CamlQuery
$ListItems = $List.GetItems($Query)
$context.Load($ListItems)
$context.ExecuteQuery()
# Turn item into a catch array
$ListItemCollection = #()
ForEach ($item in $ListItems)
{
$propertiesValues = New-Object PSObject
$currentItem = $item
$item.FieldValues.Keys | Where {$_ -ne "MetaInfo"} | ForEach {Add-Member -InputObject $propertiesValues -MemberType NoteProperty -Name $_ -Value $currentItem[$_]}
$ListItemCollection += $propertiesValues
Remove-Variable propertiesValues
}
Return $ListItemCollection
}
Now I'm building a new function and trying to use one list (which is querying a sharepoint folder) to update a sharepoint list. I query the directory with the get-sharepointlistdata, then loop through the results to add new entries if something is missing. This whole process works without issue. I'm trying to add a step in to update for any changes, but the function keeps failing on $list.Items.GetByID($index) throwing the error "You cannot call a method on a null-valued expression.":
Function Set-SharepointListData
{
Param(
[Parameter(Mandatory = $true)]
$cred,
[Parameter(Mandatory = $true)]
$sitename,
$siteUrl = "https://$env:userdomain.sharepoint.com/$sitename",
[Parameter(Mandatory = $true)]
$ListName,
[Parameter(Mandatory = $true)]
[int]$Index,
[Parameter(Mandatory = $true)]
$Time
)
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
# Bind to site collection
$Context = Get-SharepointContext -siteUrl $siteUrl -cred $cred
# Get List
$List = $Context.Web.Lists.GetByTitle($ListName)
$Context.Load($List)
$Context.ExecuteQuery()
# Select Single List Item
$ListItem = $List.Items.GetById($index)
$ListItem["Created"] = $time
$ListItem.Update();
$Context.ExecuteQuery();
}
I'm certain I'm overlooking something obvious here... anyone have any ideas?

The $Context.Web.Lists.GetByTitle($ListName) doesn't return the Items of the list. You have to load the Items... normally done via caml query. See here - Although the sample is in C# it should get you started.
Actually I rather suggest you to use PnPPowershell, there are plenty of cmdlets to work with Sharepoint.

Related

Format date download issue in PowerShell

I have a files in my SharePoint site that look something like this
ShardReportsFull/ShardData-Full-2022-03-28-03.00.12AM.csv
ShardReportsFull/ShardData-Full-2022-03-27-53.00.12AM.csv
ShardReportsFull/ShardData-Full-2022-03-25-34.00.12AM.csv
I'm just wondering how can I download the latest file. I have tried passing a date like this but the problem is the uploaded file name format. it has a time where is not consistent so I can't just pass a date like this so I need to find a way to download the latest file instead.
$Date = Get-Date
$ShardDate = $Date.ToString("yyyy-MM-dd")
$Global:ShardListURL = "/sites/msteams_88c7ed/ShardReportsFull/ShardData-Full-"+$ShardDate+".csv"
$Global:shardListCSV = "C:\scripts\Re-LitHold-OneDrive\Download-Files\ShardData-Full-"+$ShardDate+".csv"
$Global:SiteURL = "https://company.sharepoint.com/sites/"
$Global:ShardListURL = "/sites/msteams_88c7ed/ShardReportsFull/ShardData-Full-2022-03-27-03.00.11AM.csv"
$Global:shardListCSV = "C:\scripts\OneDrive\Download-Files\ShardData-Full-2022-03-27-03.00.11AM.csv"
Function Download-FileFromLibrary() {
param
(
[Parameter(Mandatory = $true)] [string] $SiteURL,
[Parameter(Mandatory = $true)] [string] $SourceFile,
[Parameter(Mandatory = $true)] [string] $TargetFile
)
Try {
#Setup Credentials to connect
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Global:adminUPN, $Global:adminPwd)
#Setup the context
$Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Ctx.Credentials = $Credentials
#sharepoint online powershell download file from library
$FileInfo = [Microsoft.SharePoint.Client.File]::OpenBinaryDirect($Ctx, $SourceFile)
$WriteStream = [System.IO.File]::Open($TargetFile, [System.IO.FileMode]::Create)
$FileInfo.Stream.CopyTo($WriteStream)
$WriteStream.Close()
Write-host -f Green "File '$SourceFile' Downloaded to '$TargetFile' Successfully!" $_.Exception.Message
}
Catch {
write-host -f Red "Error Downloading File!" $_.Exception.Message
}
}
Download-FileFromLibrary -SiteURL $Global:SiteURL -SourceFile $Global:ShardListURL -TargetFile $Global:shardListCSV

Custom function, value from pipeline

This a function for changing local user password on a remote machine. I'd like to make it work with a value from pipeline. This works:
$x= #()
$x += Set-UserPassword -ComputerName smz0017d -User localadmin -NewPassword "1"
$x += Set-UserPassword -ComputerName smz0027d -User localadmin -NewPassword "2"
$x | Out-GridView
But with value from pipeline it doesn't. Any tips?
$x = #()
$x += [pscustomobject]#{
ComputerName = 'smzmi0027d'
User = 'localadmin'
NewPassword = 'djkufdjkuf1234'
}
$x += [pscustomobject]#{
ComputerName = 'smzmi0027d'
User = 'localadmin'
NewPassword = '1'
}
foreach ($y in $x)
{
$y | Set-UserPassword
}
The function to accept value from a pipeline. It invokes command on a remote machine and builds custom object with a result:
function Set-UserPasswordLocaly
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true,
Position = 0)]
[string]$User,
[Parameter(Mandatory = $true,
Position = 1)]
[string]$NewPassword
)
#TODO: Place script here
try
{
Set-LocalUser -Name $User -Password (ConvertTo-SecureString $NewPassword -AsPlainText -Force) -ErrorAction Stop
$pwdSetResult = "$user password has been changed"
$isSuccess = $true
}
catch
{
$pwdSetResult = ($_.Exception).Message
$isSuccess = $false
}
return [PSCustomObject]#{
'ComputerName' = $env:COMPUTERNAME
'isSuccess' = $isSuccess
'Message' = $pwdSetResult
}
}
function Set-UserPasswordRemotely
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true,
Position = 0)]
[string]$ComputerName,
[Parameter(Mandatory = $true,
Position = 1)]
[string]$User,
[Parameter(Mandatory = $true,
Position = 2)]
[string]$NewPassword
)
#TODO: Place script here
$param = #{
ComputerName = $ComputerName
ScriptBlock = ${function:Set-UserPasswordLocaly}
ArgumentList = $User, $NewPassword
ErrorAction = 'stop'
}
try
{
$invoke = Invoke-Command #param
$invokeResult = $invoke.Message
$isSuccess = $invoke.isSuccess
}
catch
{
$invokeResult = ($_.Exception).Message
$isSuccess = $false
}
return [PSCustomObject]#{
'ComputerName' = $ComputerName
'isSuccess' = $isSuccess
'Message' = $invokeResult
}
}
function Set-UserPassword
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true,
Position = 0)]
[string]$ComputerName,
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 1)]
[string]$User,
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 2)]
[string]$NewPassword,
[Parameter(Position = 3,ValueFromPipelineByPropertyName = $true)]
[string]$PasswordVersion
)
#TODO: Place script here
PROCESS
{
if ($env:COMPUTERNAME -eq $ComputerName)
{
Set-UserPasswordLocaly $User $NewPassword
}
else
{
Set-UserPasswordRemotely $ComputerName $User $NewPassword
}
}
}
The ComputerName parameter in Set-UserPassword is not configured to accept pipeline input. Change that and it'll work:
function Set-UserPassword
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 0)]
[string]$ComputerName,
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 1)]
[string]$User,
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,
Position = 2)]
[string]$NewPassword,
[Parameter(Position = 3,ValueFromPipelineByPropertyName = $true)]
[string]$PasswordVersion
)
# I'm using Write-Host to demonstrate binding behavior, replace with your actual code
process { Write-Host $PSBoundParameters }
}
PS ~> [pscustomobject]#{
>> ComputerName = 'smzmi0027d'
>> User = 'localadmin'
>> NewPassword = '1'
>> } |Set-UserPassword
>>
[NewPassword, 1] [User, localadmin] [ComputerName, smzmi0027d]

Failed to send ok during RDP automation to legal notice banner

$server = “ServerName”
$Cred1 = New-Object -TypeName pscredential -ArgumentList “UserName”,(ConvertTo-SecureString -String ‘password’ -AsPlainText -Force);
Function Connect-Mstsc {
[cmdletbinding(SupportsShouldProcess,DefaultParametersetName=’UserPassword’)]
param (
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[Alias(‘CN’)]
[string[]] $ComputerName,
[Parameter(ParameterSetName=’UserPassword’,Mandatory=$true,Position=1)]
[Alias(‘U’)]
[string] $User,
[Parameter(ParameterSetName=’UserPassword’,Mandatory=$true,Position=2)]
[Alias(‘P’)]
[string] $Password,
[Parameter(ParameterSetName=’Credential’,Mandatory=$true,Position=1)]
[Alias(‘C’)]
[PSCredential] $Credential,
[Alias(‘A’)]
[switch] $Admin,
[Alias(‘MM’)]
[switch] $MultiMon,
[Alias(‘F’)]
[switch] $FullScreen,
[Alias(‘Pu’)]
[switch] $Public,
[Alias(‘W’)]
[int] $Width,
[Alias(‘H’)]
[int] $Height,
[Alias(‘WT’)]
[switch] $Wait
)
begin {
[string]$MstscArguments = ”
switch ($true) {
{$Admin} {$MstscArguments += ‘/admin ‘}
{$MultiMon} {$MstscArguments += ‘/multimon ‘}
{$FullScreen} {$MstscArguments += ‘/f ‘}
{$Public} {$MstscArguments += ‘/public ‘}
{$Width} {$MstscArguments += “/w:$Width “}
{$Height} {$MstscArguments += “/h:$Height “}
}
if ($Credential) {
$User = $Credential.UserName
$Password = $Credential.GetNetworkCredential().Password
}
}
process {
foreach ($Computer in $ComputerName) {
$ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo
$Process = New-Object System.Diagnostics.Process
# Remove the port number for CmdKey otherwise credentials are not entered correctly
if ($Computer.Contains(‘:’)) {
$ComputerCmdkey = ($Computer -split ‘:’)[0]
} else {
$ComputerCmdkey = $Computer
}
$ProcessInfo.FileName = “$($env:SystemRoot)\system32\cmdkey.exe”
$ProcessInfo.Arguments = “/generic:TERMSRV/$ComputerCmdkey /user:$User /pass:$($Password)”
$ProcessInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
$Process.StartInfo = $ProcessInfo
if ($PSCmdlet.ShouldProcess($ComputerCmdkey,’Adding credentials to store’)) {
[void]$Process.Start()
}
$ProcessInfo.FileName = “$($env:SystemRoot)\system32\mstsc.exe”
$ProcessInfo.Arguments = “$MstscArguments /v $Computer”
$ProcessInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Normal
$Process.StartInfo = $ProcessInfo
if ($PSCmdlet.ShouldProcess($Computer,’Connecting mstsc’)) {
[void]$Process.Start()
if ($Wait) {
$null = $Process.WaitForExit()
}
}
}
}
}
connect-mstsc -ComputerName $server -Credential $Cred1 -ErrorAction stop
##below code is used to skip certificate warning###
[void][System.Reflection.Assembly]::LoadWithPartialName(‘System.Windows.Forms’)
# Get the ID of the process
$WindowsHandle = Get-Process | Where-Object { $_.ProcessName -match ‘mstsc’ } | Select-Object -ExpandProperty Id
# Activate the window
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate($WindowsHandle) | Out-Null
[System.Windows.Forms.SendKeys]::SendWait(“{TAB}”)
[System.Windows.Forms.SendKeys]::SendWait(“{TAB}”)
[System.Windows.Forms.SendKeys]::SendWait(“{TAB}”)
[System.Windows.Forms.SendKeys]::SendWait(“{ENTER}”)
##Below code needs to modified to click ok button legal notice banner
function Click-MouseButton
{
$signature=#’
[DllImport(“user32.dll”,CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
‘#
$SendMouseClick = Add-Type -memberDefinition $signature -name “Win32MouseEventNew” -namespace Win32Functions -passThru
$SendMouseClick::mouse_event(0x00000002, 0, 0, 0, 0);
$SendMouseClick::mouse_event(0x00000004, 0, 0, 0, 0);
}
[system.Reflection.Assembly]::LoadWithPartialName("Remote Desktop Connection") | out-null
# Set the exactly position of cursor in some iexplore hyperlink between the (open parenthesis) below:
[System.Windows.Forms.Cursor]::Position
= New-Object System.Drawing.Point(790,675)
Click-MouseButton
Experts can someone help me to make the script works am using the above script to automate RDP connection and click ok button on legal notice screen during RDP login but script failed to send OK button,Any help much appreciated!
if above script is not able to make it then suggest some ways to automate RDP connection to the server and bypass legal notice banner

Powershell in UIPath. Throw : Unable to cast object of type 'Microsoft.Exchange.WebServices.Data.GetItemResponse' to type 'System.String'

I am trying to grab emails from Exchange using powershell in UI Path. When trying to return the items, I get the following error:
Throw : Unable to cast object of type 'Microsoft.Exchange.WebServices.Data.GetItemResponse' to type 'System.String'.
Even when I change the TypeArgument in UI Path. Currently I am using the Invoke power shell activity but I get the same issues using the RunPowershellScript activity. Below is a screenshot of my sequence in UI Path, my parameters, and my powershell script, Thank you!
Param(
[parameter()]
[string]$mailbox,
[parameter()]
[string]$password
)
try{
#https://forum.uipath.com/t/get-argument-from-an-process-with-exception-in-reframework/22537/4
function test-password(){
$Creds = new-object System.Net.networkCredential -ArgumentList $mailbox, $password
$mailFolder = "Inbox"
$itemView = 3
#[Reflection.Assembly]::LoadFile("C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll")
$ExSer = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
$ExSer.Credentials = ($Creds)
$ExSer.Url = new-object Uri("https://outlook.office365.com/EWS/Exchange.asmx")
$ExSer.AutodiscoverUrl($mailbox, {$true})
$setMailFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($ExSer,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
$logs2 += "Successfully Connected to mailbox $mailbox"
$iv = new-object Microsoft.Exchange.WebServices.Data.ItemView($itemView)
$CustomFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And)
$ifIsRead = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead,$false)
$ifFrom = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::From,"filteremail#mycompany.com")
$CustomFilter.add($ifIsRead)
$CustomFilter.add($ifFrom)
$filteredEmails = $ExSer.FindItems($setMailFolder.Id,$CustomFilter, $iv)
$psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$psPropertySet.RequestedBodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::Text;
#add-type "Microsoft.Exchange.WebServices.Data.GetItemResponse"
$ExSer.LoadPropertiesForItems($filteredEmails,$psPropertySet)
$item = $filteredEmails.Items[0]
return $mailbox, $item
}
test-password
}
catch{
Throw
}
I commented out the load dll as it seemed to work without it and was throwing a similar error when it hit it. The code seems to throw an error when it hits $Exser.LoadPropertyItems. I have also tried switching to Exchange 2007 etc. To clarify, when running purely powershell outside of UI Path, this code works just fine.
I figured it out....I was just being a dingus. It was trying to load an object, which was breaking it. If I saved it as a variable and then converted it within my script and returned what I needed as a string....guess what? It could understand the string. Below is my updated powershell.
Param(
[parameter()]
[string]$mailbox,
[parameter()]
[string]$password
)
try{
#https://forum.uipath.com/t/get-argument-from-an-process-with-exception-in-reframework/22537/4
function test-password(){
$Creds = new-object System.Net.networkCredential -ArgumentList $mailbox, $password
$mailFolder = "Inbox"
$itemView = 50
# Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
#[Reflection.Assembly]::LoadFile("C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll")
$ExSer = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
$ExSer.Credentials = ($Creds)
$ExSer.Url = new-object Uri("https://outlook.office365.com/EWS/Exchange.asmx")
$ExSer.AutodiscoverUrl($mailbox, {$true})
$setMailFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($ExSer,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
$logs2 += "Successfully Connected to mailbox $mailbox"
$iv = new-object Microsoft.Exchange.WebServices.Data.ItemView($itemView)
$CustomFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And)
$ifIsRead = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead,$true)
$ifFrom = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::From,"emailtofilterby#mycompany.com")
$CustomFilter.add($ifIsRead)
$CustomFilter.add($ifFrom)
$filteredEmails = $ExSer.FindItems($setMailFolder.Id,$CustomFilter, $iv)
$psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$psPropertySet.RequestedBodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::Text;
#add-type "Microsoft.Exchange.WebServices.Data.GetItemResponse"
$FilteredEmailitems = $ExSer.LoadPropertiesForItems($filteredEmails,$psPropertySet)
$EmailBody = $FilteredEmailitems.Item(0) | Select-Object -ExpandProperty Item | Select-Object -ExpandProperty Body | Select-Object -ExpandProperty Text
return $EmailBody
}
test-password
}
catch{
Throw
}

Can I make a parameter set depend on the value of another parameter?

Let's say I have a function like:
function Authenticate
{
param
(
[ValidateSet('WindowsAuthentication','UsernameAndPassword')][string] $AuthenticationType,
[Parameter(ParameterSetName='Set1')][string] $Username,
[Parameter(ParameterSetName='Set1')][string] $Password
)
..
}
And I would like to make the parameter set to be mandatory when $AuthenticationType = 'UsernameAndPassword' but also so that it cannot be used if $AuthenticationType = 'WindowsAuthentication'.
It this even possible in PowerShell?
Using the link from Tim Ferrill's answer, I created the following function to help create dynamic parameters:
function New-DynamicParameter
{
[CmdletBinding(DefaultParameterSetName = 'Core')]
param
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][string] $Name,
[Parameter(Mandatory = $true, ParameterSetName = 'Core')][Parameter(Mandatory = $true, ParameterSetName = 'ValidateSet')][type] $Type,
[Parameter(Mandatory = $false)][string] $ParameterSetName = '__AllParameterSets',
[Parameter(Mandatory = $false)][bool] $Mandatory = $false,
[Parameter(Mandatory = $false)][int] $Position,
[Parameter(Mandatory = $false)][bool] $ValueFromPipelineByPropertyName = $false,
[Parameter(Mandatory = $false)][string] $HelpMessage,
[Parameter(Mandatory = $true, ParameterSetName = 'ValidateSet')][string[]] $ValidateSet,
[Parameter(Mandatory = $false, ParameterSetName = 'ValidateSet')][bool] $IgnoreCase = $true
)
process
{
# Define Parameter Attributes
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.ParameterSetName = $ParameterSetName
$ParameterAttribute.Mandatory = $Mandatory
$ParameterAttribute.Position = $Position
$ParameterAttribute.ValueFromPipelineByPropertyName = $ValueFromPipelineByPropertyName
$ParameterAttribute.HelpMessage = $HelpMessage
# Define Parameter Validation Options if ValidateSet set was used
if ($PSCmdlet.ParameterSetName -eq 'ValidateSet')
{
$ParameterValidateSet = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $ValidateSet -Strict (!$IgnoreCase)
}
# Add Parameter Attributes and ValidateSet to an Attribute Collection
$AttributeCollection = New-Object Collections.ObjectModel.Collection[System.Attribute]
$AttributeCollection.Add($ParameterAttribute)
$AttributeCollection.Add($ParameterValidateSet)
# Add parameter to parameter list
$Parameter = New-Object System.Management.Automation.RuntimeDefinedParameter -ArgumentList #($Name, $Type, $AttributeCollection)
# Expose parameter to the namespace
$ParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$ParameterDictionary.Add($Name, $Parameter)
return $ParameterDictionary
}
}
And solved my particular problem in the following way:
function Authenticate
{
param
(
[ValidateSet('WindowsAuthentication','UsernameAndPassword')][string] $AuthenticationType,
)
DynamicParam
{
if ($AuthenticationType -eq 'UsernameAndPassword')
{
New-DynamicParameter Username [string] -Mandatory $true
New-DynamicParameter Password [string] -Mandatory $true
}
}
...
}
It became unneeded to have a parameter set when using Dynamic Parameter so I removed the parameter set.
You can do this using DynamicParam. I saw a decent post on this recently here.
DynamicParam {
if ($AuthenticationType -eq 'UsernameAndPassword') {
#create ParameterAttribute Objects for the username and password
$unAttribute = New-Object System.Management.Automation.ParameterAttribute
$unAttribute.Mandatory = $true
$unAttribute.HelpMessage = "Please enter your username:"
$pwAttribute = New-Object System.Management.Automation.ParameterAttribute
$pwAttribute.Mandatory = $true
$pwAttribute.HelpMessage = "Please enter a password:"
#create an attributecollection object for the attributes we just created.
$attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
#add our custom attributes
$attributeCollection.Add($unAttribute)
$attributeCollection.Add($pwAttribute)
#add our paramater specifying the attribute collection
$unParam = New-Object System.Management.Automation.RuntimeDefinedParameter('username', [string], $attributeCollection)
$pwParam = New-Object System.Management.Automation.RuntimeDefinedParameter('password', [string], $attributeCollection)
#expose the name of our parameter
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('username', $unParam)
$paramDictionary.Add('password', $pwParam)
return $paramDictionary
}
}
Process {
$PSBoundParameters.username
$PSBoundParameters.password
}