Powershell loop until the output is one line - azure

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.

Related

PowerShell :: Microsoft.Azure.Commands.Sql.Database.Model.AzureSqlDatabaseModel.DatabaseName [duplicate]

This question already has answers here:
How can you use an object's property in a double-quoted string?
(5 answers)
Closed 5 months ago.
I wrote a script that allows me to query the whole Azure database park:
#$ErrorActionPreference = 'SilentlyContinue'
# Connect to Azure
$azureAccount = Connect-AzAccount
# Get Azure Access Token (we will use this to query the databasees)
#$azureToken = Get-AzAccessToken -ResourceUrl https://database.windows.net
$access_token = (Get-AzAccessToken -ResourceUrl https://database.windows.net).Token
# Queries will be picked up from here
$folderPath = '.\Queries'
# Choose how to format each date ("yyyy-MM-dd") or ("yyyy-MM-dd HH:mm:ss")
$DateTime = (Get-Date).ToString("yyyy-MM-dd")
# List Azure Sunscriptions
Get-Azsubscription | ForEach-Object -Begin { $a = 1 } -Process {"$a $($_.Name)"; $a++}
$SubscriptionChoice = Read-Host -Prompt "Copy/paste the name of the Subscription that you want to investigate. If more than one separate them by a coma, Type `"All`" if you want to target all of them"
# Iterate into subscriptoins and print names
foreach ($gs in $SubscriptionChoice) {
Select-Azsubscription -Subscription "$gs" | Out-Null
Write-Host "Let's browse into Azure Sunscription: " -NoNewline
Write-Host (Get-AzContext).Subscription.Name -ForegroundColor green
# Fins all Azure SQL Server
Get-AzSqlServer | ForEach-Object -Begin { $a = 1 } -Process {"$a $($_.ServerName)"; $a++}
$SqlServerChoice = Read-Host -Prompt "Copy/paste the name of the SQL Server that you want to investigate. If more than one separate them by a coma, Type `"All`" if you want to target all of them"
if ($SqlServerChoice = "All"){
$SqlServerChoice = Get-AzSqlServer
}
Foreach ($server in $SqlServerChoice){
$DatabaseChoice = Get-AzSqlDatabase -ServerName $server.ServerName -ResourceGroupName $server.ResourceGroupName | Where-Object DatabaseName -NE "master"
Foreach ($database in $DatabaseChoice){
(Get-ChildItem $folderPath | sort-object {if (($i = $_.BaseName -as [int])) {$i} else {$_}} ).Foreach{
Invoke-Sqlcmd -ServerInstance $server.FullyQualifiedDomainName -Database $database.DatabaseName -AccessToken $access_token -InputFile $psitem.FullName | Export-Csv -Path ".\Results\$psitem.csv" -Append -NoTypeInformation
write-host "Executing $psitem on $database.DatabaseName"
}
}
}
}
However each time the query is executed against a database the Write-Hosts returns:
Executing DTU_to_vCore.sql on Microsoft.Azure.Commands.Sql.Database.Model.AzureSqlDatabaseModel.DatabaseName
Here a picture:
This Write-Hosts comes from the line:
write-host "Executing $psitem on $database.DatabaseName"
In which you can find the two variables:
$psitem : which is the name of the file that contains the query
$database.DatabaseName : which should be the database name but instead of printing the database name is printing Microsoft.Azure.Commands.Sql.Database.Model.AzureSqlDatabaseModel.DatabaseName
Why one of the two variable is not interpreted?
You need to encapsulate your variable property in a subexpression operator $().
write-host "Executing $psitem on $($database.DatabaseName)"
This is because only simple variables get expanded in an expandable string.
References
Only simple variable references can be directly embedded in an
expandable string. Variables references using array indexing or member
access must be enclosed in a subexpression.
Source: about_Quoting_Rules
Subexpression operator $( )
Returns the result of one or more statements. For a single result,
returns a scalar. For multiple results, returns an array. Use this
when you want to use an expression within another expression. For
example, to embed the results of command in a string expression.
PS> "Today is $(Get-Date)"
Today is 12/02/2019 13:15:20
PS> "Folder list: $((dir c:\ -dir).Name -join ', ')"
Folder list: Program Files, Program Files (x86), Users, Windows
Source: about_Operators

How to make sure string is being given a proper format

I'm coding a progress bar for my script, and I've run into an issue where a string is being fed data in an incorrect format.
foreach ($User in $Users) {
#Set UserPrincipalName for current user
$upn = $User
#Display progress bar
$percentage = [math]::Round($loopcount / $maxcount *100)
$message = "Applying changes for $upn ($loopcount of $maxcount)" -f $percentage
Write-Progress -Activity $message -ErrorAction SilentlyContinue -PercentComplete ($percentage) -Status "Progress $percentage% :"
This is the main part of the code that makes up the progress bar (there's more, but that's irrelevant). It works great when it is being fed user identities (via $User) like "delat" and "damo", but when it gets to a user called "DiscoverySearch {D919BA05-46A6-415f-80AD-XXXXXXXXXXXXXXXX}" it understandably throws a "Error formatting a string" error.
How do I go about solving this issue?
Edit: Below is the source of $User, in case that's needed.
$MailboxRegionList = Get-Mailbox | Get-MailboxRegionalConfiguration
$Users += $MailboxRegionList | Select-Object -ExpandProperty Identity
This happens, as -f $percentage is not doing what you'd expect. It is not going to format the message as percentage a value.
$message = "Applying changes for $upn ($loopcount of $maxcount)" -f $percentage
The format operator, -f will fill in a value in string. The placeholder is to be marked with curly brackets {}, which your string doesn't usually have. When the $user is DiscoverySearch {D919BA05-46A6-415f-80AD-XXXXXXXXXXXXXXXX}, it contains curly brackets. Now, the curly brackets should follow .Net composite formatting syntax, and a GUID in account name does not do that.
As for a fix, try
$message = "Applying changes for $upn ($loopcount of $maxcount) $percentage"

Cut variable into multiple Strings

I want to write a script that renames all NICs on a Server 2012 R2.
Currently it looks like this with each one:
$NIC = Get-WMIObject -Class Win32_NetworkAdapter -Filter "NetconnectionID='Embedded LOM 1 Port 1'"
$NIC.NetconnectionID = 'Physical 1'
$NIC.Put()
Now I want to use this for different Servers and therefore I have to get the NetconnectionID from a variable.
So far I have put the NICs into a variable:
$NICS = Get-NetAdapter | select name
Now when just issuing the command $NICS it shows the list of names, but since I want to rename however many of NICs I have individually I have to break the variable down into different strings. It would be awesome if it would even count the amount and then implement my script with an if statement or foreach!
But for now I would be happy with a solution to rename a specific amount (in my case it's four).
$NICS = Get-NetAdapter | select name
for ($i = 0; $i -lt $NICS.Count; $i++) {
Write-Host (($NICS[$i]) -replace("#{name=","") -replace("}",""))
}
That is a good starting point I think.
Use foreach for that:
$NICS = Get-NetAdapter | select name
foreach ($n in $nics)
{
write-host "Nick name " $n.name
}

Active Directory Powershell - forest-wide search script using .csv list of users

I am looking for a bit of help, hope nobody will bash me for being an ignorant.
Not that long ago I became something of an AD admin, organisation is big so the tasks vary. I can easily complete what I require via Powershell or snap-ins in most cases.
However I have a task on my hands that exceed my "creativity". I have a list of over 10 000 users in .csv which I need to look up in on-premises AD if they exist. My two problems are:
-I am very new to scripting and getting increasingly frustrated that I can't comprehend it and make my scripts work as I need them to
-Deadline for this task and other responsibilities give me little time to read more on scripting basics and learn. As such I am in most cases forced to look for script snippets on the web and modify them a bit to meet my needs. This worked up until now as the script I have on my hands is a bit too complex for me.
Biggest problem I was facing so far is creating a forest-wide search. My organization have a single root domain and 4 child domains. When running a simple foreach loop a like the one below:
ForEach ($User in (Import-Csv c:\users\public\users.csv))
{ If (Get-ADUser $User.mail -server GLOBALCATALOGADDRESS:xxxx)
{ Write-Host "User found: $($User.mail)"
}
Else
{ Write-Host "User not found: $($User.mail)"
}
}
It searches only domain to which my computer is connected.
So I managed to find and modify a forest-wide search script and came up with following:
#Get Domain List
$objForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$DomainList = #($objForest.Domains | Select-Object Name)
$Domains = $DomainList | foreach {$_.Name}
$User = Import-CSV c:\users\public\users.csv
#Act on each domain
foreach($Domain in ($Domains))
{
Write-Host "Checking $Domain" -fore red
$ADsPath = [ADSI]"LDAP://$Domain"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher($ADsPath)
#The filter
Foreach($mail in($User))
{
$objSearcher.Filter = "(&(objectCategory=user)(mail=$User.mail))"
$objSearcher.SearchScope = "Subtree"
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
{
$objArray = $objResult.GetDirectoryEntry()
write-host $objArray.mail
}
}
}
The script seems to be good in its original form (found here: http://powershell.nicoh.me/powershell-1/active-directory/forest-wide-object-searches) and searches well with wildcard and single parameter as filter.
However I have no idea what am I missing to make it search for every email address I have in .csv and to make it return information whether or not user with such mail was found.
Script itself runs but given the time it takes and blank output it feels like it searches for only one user. I am 100% sure that at least one user from the list exists in on-prem AD.
Any suggestions are very welcome.
Thanks for your attention.
[EDIT]
Final script:
#Get Domain List and load user e-mails from file
$objForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$DomainList = #($objForest.Domains | Select-Object Name)
$Domains = $DomainList | foreach {$_.Name}
$Users = Import-CSV c:\users\public\users.csv
#Act on each domain
foreach($Domain in ($Domains))
{
Write-Host "Checking $Domain" -fore red
Foreach($mail in ($Users.mail))
{
Get-ADUser -filter {mail -eq $mail} -Server $domain -properties mail | select mail
}
}
Do yourself a favour and download AD Powershell module: http://blogs.msdn.com/b/rkramesh/archive/2012/01/17/how-to-add-active-directory-module-in-powershell-in-windows-7.aspx
You will then be able to simplify your code and run things like this, making your task much clearer:
...
foreach($Domain in ($Domains))
{
Write-Host "Checking $Domain" -fore red
Foreach($mail in ($User.mail))
{
Get-ADUser -filter {mail -eq $mail} -Server $domain -Properties mail |
select-object -ExpandProperty mail
}
}
...
More on AD PS cmdlets: http://technet.microsoft.com/en-us/library/ee617195.aspx
Use -LDAPfilter & point the -Server to GC.
Get-ADUser -Server DC01.Contoso.com:3268
-Ldapfilter "(ObjectClass=user)(mailnickname=David)"
The above command will search the GC DC01.contoso.com for all the users that their Alias/mailnickname is David.
Is is enough to contact the Domain itself instead of a DC of the domain. Thus this shoud also work
get-aduser -Filter {mailnickname -eq "David") -Server contoso.com:3268

Export AD User Properties to CSV

I want to interrogate the client' AD to see which users are missing property values such as telephone number ahead of User Profile Sync in SharePoint 2013, the script works but now I need to add a little "magic" in order to create my csv file... I have added a comment below to indicate where I think this "magic" should go!
# get Users and groups
#Get the User from AD
$domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$root = $domain.GetDirectoryEntry()
$search = [System.DirectoryServices.DirectorySearcher]$root
#$search.Filter = "(&(objectCategory=User)(samAccountName=$userName))"
# get a list of the users but not the sp_ service accounts.
$search.Filter = "(&(objectCategory=User) (!(name=SP_*)) )"
$search.SearchScope ="subtree"
# determine the properties we want back
$colPropList = "name", "jobTitle", "telephoneNumber","mail", "department" , "thumbnailPhoto"
foreach ($i in $colPropList){$search.PropertiesToLoad.Add($i)}
$result = $search.FindAll()
if ($result -ne $null)
{
foreach ( $entry in $result )
{
# this works though I might have incorrect names for some of the properties
$user = $entry.Properties;
$user.name
$user.department
$user.jobTitle
$user.telephoneNumber
$user.mail
$user.thumbnailPhoto
*# !!!!!!This is where I need help!!!!!
# as my $user is effectively an object then I should be able to to use it to create a an object with Add-Member
# Do I breaker down the $user properties and create another object with name values ???*
foreach ($o in $user)
{
Add-Member -InputObject $psObject -MemberType NoteProperty -Name $o -Value $o
}
}
$psObject | Export-Csv c:\dev\aduserList.csv -NoTypeInformation
}
I'm not familiar with directorysearcher / adsi, but if you're migrating to SharePoint 2013 I'd guess you also have a computer with PowerShell.
In that case you should use Microsofts ActiveDirectory module (installed on servers and through RSAT for clients) if you have a 2008 DC or 2003 with Active Directory Web Service.
You could also use Quest ADRoles Module.
PowerShell cmdlets are much easier to use for AD administration. You could then shorten down your script to one line(this is the ActiveDirectory module from Microsoft):
Get-ADUser -LDAPFilter "(!(name=SP_*))" -Properties Name, Title, OfficePhone, Mail, Department, thumbnailPhoto | Select-Object Name, Title, OfficePhone, Mail, Department, thumbnailPhoto | Export-Csv c:\dev\aduserList.csv -NoTypeInformation
I'm not sure if the thumbnailphoto part works as I haven't used that attribute before.
Something like this should work:
$search.FindAll() | select -Expand Properties |
select #{n='name';e={$_.name}},
#{n='department';e={$_.department}},
#{n='jobTitle';e={$_.jobtitle}},
#{n='telephoneNumber';e={$_.telephonenumber}},
#{n='mail';e={$_.mail}},
#{n='thumbnailPhoto';e={$_.thumbnailphoto}} |
Export-Csv c:\dev\aduserList.csv -NoTypeInformation
Note that the properties used in the expression section of the calculated property (#{n='name';e={expression}}) must be lowercased:
#{n='thumbnailPhoto';e={$_.thumbnailphoto}}
Using the Get-ADUser cmdlet from the ActiveDirectory module as Frode F. suggested is a more convenient way to get the information you want, but it requires that the AD PowerShell module is installed on the computer where it's used, and that the AD Web Services are installed and running on a DC.

Resources