Powershell function in while loop doesn't output result first time but does output second time,twice - azure

As you can see I have 5 options to select once the script runs. When I press 1, option 1 selected" gets printed but the function get_all_tags doesn't get called out and doesn't print list of tags. Second time around it prints the results from the function but repeats the result twice.
The weird thing about this is that the its inconsistent. I would run the script and in my first try pressing 1, sometimes, I would get the result from the function get_all_tags .When all the functions are tested on their own, it works perfectly fine. The while loop seems to have caused something irregular within and not sure how I can fix it.
$options=0
$iteration=0
while($options -ne 5)
{
$options = read-host -prompt "
Press
1. to search for all the tags running on Azure Virtual Machine(s)
2. to search for resource using tag name
3. to search for specific resource
4. to search for value of a tag name
5. to exit
"
if ($options -eq 1){
write-host "option 1 selected"
get_all_tags
#give option to exit or go back to the main menu
$iteration++
$iteration
}
elseif ($options -eq 2){
get_resource_with_tag_name $inputTagName
#give option to exit or go back to the main menu
}
elseif ($options -eq 3){
get_resource_tags $inputResource
#give option to exit or go back to the main menu
}
elseif ($options -eq 4){
get_keys_value $inputTagKey
#give option to exit or go back to the main menu
}
}
Function get_resource_tags($resourceName)
{
$inputResource = read-host -prompt 'Write a resource name you wish to search with all associated tags'
return (get-azresource -ResourceGroupName $inputResource).Tags
}
Function get_resource_with_tag_name($tagName){
$inputTagName = read-host -prompt 'Write a tag name you wish to search associated with resource'
return (get-AzResource -TagName $inputTagName).Name
}
Function get_all_tags{
return get-aztag
}
Function get_keys_value($tagKeyName){
$inputTagKey = read-host -prompt 'Write a tag name'
$result = (get-aztag -Detailed $inputTagKey).Values
$final = $result | ft -property #{n="Tag Value";e={$_.name}},count
return $final
}

PowerShell is an interpreter language as opposed to a compiler language. Hence, the location of your Functions is messing with your results. They should be declared before they are being called.
In your case, during the 1st run, the while loop is not aware of the functions defined below it. Once you run it completely, the functions are loaded in memory and hence, the second run will probably give you the intended results.

thanks #sid for responding and pointing out the difference. Despite having made the changes, Options 1 and 3 still do that same whilst 2 and 4 always print out results.
Function get_all_tags{
return get-aztag
}
Function get_resource_with_tag_name($tagName){
$inputTagName = read-host -prompt 'Write a tag name you wish to search associated with resource'
return (get-AzResource -TagName $inputTagName).Name
}
Function get_resource_tags($resourceName)
{
$inputResource = read-host -prompt 'Write a resource name you wish to search with all associated tags'
$iteration++
$iteration
return (get-azresource -ResourceGroupName $inputResource).Tags
}
Function get_keys_value($tagKeyName){
$inputTagKey = read-host -prompt 'Write a tag name'
$result = (get-aztag -Detailed $inputTagKey).Values
$final = $result | ft -property #{n="Tag Value";e={$_.name}},count
return $final
}
$options=0
$iteration=0
while($options -ne 5)
{
$options = read-host -prompt "
Press
1. to search for all the tags running on Azure Virtual Machine(s)
2. to search for resource using tag name
3. to search for specific resource
4. to search for value of a tag name
5. to exit
"
if ($options -eq 1){
write-host "option 1 selected"
get_all_tags
#give option to exit or go back to the main menu
}
elseif ($options -eq 2){
get_resource_with_tag_name $inputTagName
#give option to exit or go back to the main menu
}
elseif ($options -eq 3){
write-host "hello"
get_resource_tags $inputResource
#give option to exit or go back to the main menu
}
elseif ($options -eq 4){
get_keys_value $inputTagKey
#give option to exit or go back to the main menu
}
}

Related

Issue with code not tentering the if loop in PowerShell

I think this is dumb question but needs attention, cause I got stuck with it for some long time. I have some PowerShell code which needs help. In this code, I pass the input '1' for switch condition and 'True' for the next prompt, it is entering the 1st if condition, but not entering the 2nd if condition, even if I pass $prd's input as foo.
Have tried by declaring the variable $prd="Global", still not working.
[String]$prd = Read-Host "Enter the software to update"
[bool]$upg = $true
$param = Read-Host -Prompt "Enter the input for upgrade"
switch ($param) {
0 {
# Some condition
}
1 {
[String]$sftup = "Going for sftwre updte"
$sftd_up = Read-Host -Prompt $sftup
if ($sftd_up -eq $upg) { # -------->1st if
Write-Host "foo upgrade"
if ($prd -eq "foo") { # ----------> 2nd if
$nameprd = New-Item -Path "D:\Temp" -ItemType "directory"
}
}
}
}
It works for me. (The new-item line I put in single quotes.) The third answer could be any string.
Enter the software to update: foo
Enter the input for upgrade: 1
Going for sftwre updte: true
foo upgrade
$nameprd = New-Item -Path "D:\Temp" -ItemType "directory"

Powershell loop until the output is one line

What i am trying to achieve is that if the output is one line and that that line gets written away in a variable. This is the code i have right now:
Connect-AzureRmAccount
(get-azurermresourcegroup).ResourceGroupName
$filter = Read-Host -Prompt "Please filter to find the correct resource group"
$RGName = get-azurermresourcegroup | Where-Object { $_.ResourceGroupName -match $filter }
$RGName.resourcegroupname
this code filters one time and after that it writes all the lines away underneath each other so the results are this:
ResourceGroup-Test
ResourceGroup-Test-1
ResourceGroup-Test-2
but the preferred output is to keep filtering until one is left
Out-GridView
but the preferred output is to keep filtering until one is left
Depending on what the running user chooses for filters this could be a punishing approach / needlessly complicated. If you only want one result how about we instead use something like Out-GridView to allow the user to select one result from their chosen filter.
$filter = Read-Host -Prompt "Please filter to find the correct resource group"
$RGName = get-azurermresourcegroup |
Where-Object { $_.ResourceGroupName -match $filter } |
Out-GridView -OutputMode Single
$RGName.resourcegroupname
You could have used -PassThru but that allows for multiple selections. -OutputMode Single. So this would still have the potential for making a huge selection set if $filter was too vague but this is a simple way to ensure you get one result. Another caveat is that the user could click Cancel. So you might still need some loop logic: do{..}until{}. That depends on how resilient you want to make this process.
Choice
If Out-GridView is not your speed. Another option would be to make a dynamic choice system using $host.ui.PromptForChoice. The following is an example that allows users to choose a subfolder from a collection.
$possibilities = Get-ChildItem C:\temp -Directory
If($possibilities.Count -gt 1){
$title = "Folder Selection"
$message = "Which folder would you like to use?"
# Build the choices menu
$choices = #()
For($index = 0; $index -lt $possibilities.Count; $index++){
$choices += New-Object System.Management.Automation.Host.ChoiceDescription ($possibilities[$index]).Name
}
$options = [System.Management.Automation.Host.ChoiceDescription[]]$choices
$result = $host.ui.PromptForChoice($title, $message, $options, 0)
$selection = $possibilities[$result]
}
$selection
You should be able to adapt that into your code much in the same way that I suggested with Out-GridView. Be careful though about this approach. Too many options will clutter the screen.

Change List Field with Powershell in SharePoint Online

I'm attempting to change the field contents for multiple entries in a list. So far I've gotten to the point that I can edit the list, add columns really, but can't find anything on how to edit the field text. Here is what I have currently:
EDIT:
I've found a bunch of info for 2010 which isn't applicable but I've updated the code to almost get there. I'm getting 'null array' errors when I connect to the list now. I'm hopeful because I'm able to connect, but still can't get the field to change. I've updated my if statement as well to what is I believe a better format.
#Load necessary module to connect to SPOService
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") | Out-Null
#Login Information for script
$User = "user#email.com"
$Pass = "password"
$creds = New-Object System.Management.Automation.PSCredential($User, (ConvertTo-SecureString $Pass -AsPlainText -Force));
#Connect to SharePoint Online service
Write-Host "Logging into SharePoint online service." -ForegroundColor Green
Connect-SPOService -Url https://site-admin.sharepoint.com -Credential $creds
#Get the Necessary List
Write-Host "Getting the required list." -ForegroundColor Green
$WebUrl = 'https://site.sharepoint.com/'
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($WebUrl)
$List = $Context.Web.Lists.GetByTitle("VacationRequestForm")
#Edit existing list items
$items = $List.items
foreach($item in $items)
{
if($item["Export Flag"] -eq "New")
{
Write-Host "Changing export flags to Complete." -ForegroundColor Green
$item["Export Flag"] = "Complete"
$item.Update()
}
}
Write-Host "Your changes have now been made." -ForegroundColor Green
I am guessing you have trimmed the script since you are missing things like defining $context and such. You don't have any ExecuteQuery() calls.
MSDN doc on SP 2013 CSOM List Item tasks, which has C# examples of what you need and can be translated to PowerShell.
It generally looks like you have everything but if you could include your whole script I can try and run your script directly.
EDIT: with the updates here is the code that you need
#Load necessary module to connect to SPOService
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") | Out-Null
#Login Information for script
$User = "user#email.com"
$Pass = "password"
$WebUrl = "https://site.sharepoint.com/"
#Connect to SharePoint Online service
Write-Host "Logging into SharePoint online service." -ForegroundColor Green
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($WebUrl)
$Context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($User, (ConvertTo-SecureString $Pass -AsPlainText -Force))
#Get the Necessary List
Write-Host "Getting the required list." -ForegroundColor Green
$List = $Context.Web.Lists.GetByTitle("VacationRequestForm")
$Query = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery(100);
$Items = $List.GetItems($Query);
$Context.Load($Items);
$Context.ExecuteQuery();
#Edit existing list items
foreach($item in $Items)
{
if($item["Export_x0020_Flag"] -eq "New")
{
Write-Host "Changing export flags to Complete for Id=$($item.Id)." -ForegroundColor Green
$item["Export_x0020_Flag"] = "Complete"
$item.Update()
$Context.ExecuteQuery();
}
}
Write-Host "Your changes have now been made." -ForegroundColor Green
You had a space in your Export Flag name and SharePoint will not have that in the name of the field. By default it will replace that with a _x0020_ string. This value will be based on the first name of the field. So if you change this field name in the future, you will still refer to it as 'Export_x0020_Flag' in this script. I am doing the .ExecuteQuery() for each update but you could do this once at the end of the loop. There is also the limit of 100 records in the query. If you only want records with "New" you should change the CamlQuery to just pull those records back.

Sharepoint Powershell update every second list item

What I need to do is write a Powershell script withc would update every Second item in list. As I see it, i need to use somethin like this:
$web = Get-SPWeb http://YourServer/
$list = $web.Lists["list name"]
foreach ($item in $list.Items)
{
$item["column1"] = "New value";
$item["column2"] = "New value";
$item.Update();
}
And add If(itemID) % 2 =0;
Am I right? If I am, how can i get ID of List Items.
Thank you in advance.
Well if you only need to update every second item, and you don't care about the items you are editing, (using your code) you can simply do this:
$web = Get-SPWeb http://YourServer/
$list = $web.Lists["list name"]
$i = 0
foreach ($item in $list.Items)
{
if($i % 2 -eq 0)
{
$item["column1"] = "New value";
$item["column2"] = "New value";
$item.Update();
}
$i += 1
}

Strange error with NewWebPage - SP 2010 Foundation

I'm having a strange issue here using the NewWebPage xml command in SP 2010 Foundation. The below code works does not work
cls
Write-Host "Production Manager v8.0 Deployment Utility" -ForegroundColor red
Write-Host ""
#Starting the script. Lets see if we can find the configuration and map files first.
Write-Host -NoNewline "Checking for the Configuration.xml, PageMap.xml and PageTemplate.xml files: " -ForegroundColor white
if((Test-Path "Configuration.xml") -and (Test-Path "PageMap.xml") -and (Test-Path "PageTemplate.xml"))
{
Write-Host "FOUND" -ForegroundColor green
}
else
{
Write-Host "NOT FOUND" -ForegroundColor red
Write-Host "Check for the necessary files and try again."
Write-Host ""
Write-Host ""
exit
}
Write-Host "Reading Configuration.xml"
[xml]$Configuration = Get-Content Configuration.xml
Write-Host "Reading PageMap.xml"
[xml]$PageMap = Get-Content PageMap.xml
Write-Host "Reading from Production Manager Site: "$Configuration.Configuration.SiteConfiguration.SiteURL
#Some variables necessary for the loop
$pageCreationLoopIterations = 0
$pageLayout = ""
$pageTitle = ""
$createPageCommand = '<?xml version="1.0" encoding="UTF-8"?><Method ID="0,NewWebPage"><SetList Scope="Request">' + $productionManagerLibrary.ID + '</SetList><SetVar Name="Cmd">NewWebPage</SetVar><SetVar Name="ID">New</SetVar><SetVar Name="Type">WebPartPage</SetVar><SetVar Name="WebPartPageTemplate">' + $pageLayout + '</SetVar><SetVar Name="Overwrite">true</SetVar><SetVar Name="Title">MyPage</SetVar></Method>';
#Beginning the loop
Write-Host "Running through the PageMap file"
foreach($Page in $PageMap.Pages.Page)
{
$web = Get-SPWeb $Configuration.Configuration.SiteConfiguration.SiteURL
$productionManagerLibrary = $web.Lists | Where { $_.Title -eq "Production Manager" }
$pageName = if($Page.SelectSingleNode("PageName")) { $Page.PageName } else { $Configuration.Configuration.PageConfiguration.DefaultPageName }
$pageLayout = if($Page.SelectSingleNode("PageLayout")) { $Page.PageLayout } else { $Configuration.Configuration.PageConfiguration.DefaultPageLayout }
Write-Host 'Creating Page ' $pageName
$web.ProcessBatchData($createPageCommand)
}
But this one works just fine every time I run it:
$url = "http://mpm8/mpm";
$listname = "Production Manager"
$web = Get-SPWeb $url
$pagesLibrary = $web.Lists | Where { $_.Title -eq "Production Manager" }
$pageLayout = 8
$cmd = '<?xml version="1.0" encoding="UTF-8"?><Method ID="0,NewWebPage"><SetList Scope="Request">' + $pagesLibrary.ID + '</SetList><SetVar Name="Cmd">NewWebPage</SetVar><SetVar Name="ID">New</SetVar><SetVar Name="Type">WebPartPage</SetVar><SetVar Name="WebPartPageTemplate">' + $pageLayout + '</SetVar><SetVar Name="Overwrite">true</SetVar><SetVar Name="Title">MyPage</SetVar></Method>';
$web.ProcessBatchData($cmd)
I really cannot see anything different between the two scripts. The error I get running the first one is:
<Result ID="0,NewWebPage" Code="-2130575350">
<ErrorText>Invalid URL Parameter.
The URL provided contains an invalid Command or Value. Please check the URL again. </ErrorText></Result>
Can you help me on this one? Maybe I cannot run this thing out a foreach loop? :(
Thanks!
You have set the $createPageCommand first (before the loop), then set the variables that it needs second (in the loop).
If you run this in the ISE and step through (ie debug it), you can see the variables and their values, otherwise simply emit the values of the variables to screen as you run through the loop to ensure it is setting them correctly.
So, in your example, place the $createPageCommand within the loop and after $productionManagerLibrary is set. Then, immediately before $web.ProcessBatchData($createPageCommand) you should emit the value of $createPageCommand to ensure it is OK.
I've only eye balled this without running it, but please us know if this is the reason!

Resources