Trying to cleanup a PS script I made - azure

I work for a k-12 district and I've been writing a script to add students in after school activities to different AD security groups so I can apply access card access specifically to their later schedules (currently all students have a standard time and we've just been leaving doors unlocked for after school stuff).
Our students' after school activities are rostered in our SIS and Azure can pull all of those rosters and apply them to users. Our Access control manager only syncs with AD and not azure so I've made AD security groups and this script is attempting to sync the azure groups over to the security groups. I'm fairly new to powershell, so while I do have it working, my script is very long and took forever to figure out. I'm looking for reference on how I can shorten it
$VB = Get-AzureADGroupMember -ObjectId zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz #volleybally
$SOB = Get-AzureADGroupMember -ObjectId zzzzzzzzzzzzzzzzzzzzzzzzzzzz #Boys Soccer
$SOG = Get-AzureADGroupMember -ObjectId xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx5 #Girls Soccer
$BG = Get-AzureADGroupMember -ObjectId 7xxxxxxxxxxxxxxxxxxxxxxxxxxx #Boys Golf
$GG = Get-AzureADGroupMember -ObjectId xxxxxxxxxxxxxxxxxxxxxxxxxxxxx1c #Girls Golf
$CC = Get-AzureADGroupMember -ObjectId zzzzzzzzzzzzzzzzzzzzzzzzzzzzz #Cross Country
$FCL = Get-AzureADGroupMember -ObjectId xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx #Fall Cheerleading
$SDD = Get-AzureADGroupMember -ObjectId xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx #Speech Drama & Debate
#initiate SG Groups
$SGCC = Get-ADGroup -Identity sample group 1
$SGFC = Get-ADGroup -Identity sample group 2
$SGFB = Get-ADGroup -Identity sample group 3
$SGGO = Get-ADGroup -Identity sample group 4
$SGSO = Get-ADGroup -Identity sample group 5
$SGVB = Get-ADGroup -Identity sample group 6
# convert azure values to AD and add to AD groups
$FB= Foreach($ad in $FB) {
Get-ADUser ($ad.UserPrincipalName.Split('#')[0])
}
$VB = Foreach($ad in $VB) {
Get-ADUser ($ad.UserPrincipalName.Split('#')[0])
# your text
}
$SOB = Foreach($ad in $SOB) {
Get-ADUser ($ad.UserPrincipalName.Split('#')[0])
}
$SOG = Foreach($ad in $SOG) {
Get-ADUser ($ad.UserPrincipalName.Split('#')[0])
}
$BG = Foreach($ad in $BG) {
Get-ADUser ($ad.UserPrincipalName.Split('#')[0])
}
$GG = Foreach($ad in $GG) {
Get-ADUser ($ad.UserPrincipalName.Split('#')[0])
}
$CC = Foreach($ad in $CC) {
Get-ADUser ($ad.UserPrincipalName.Split('#')[0])
}
$FCL = Foreach($ad in $FCL) {
Get-ADUser ($ad.UserPrincipalName.Split('#')[0])
}
$SDD = Foreach($ad in $SDD) {
Get-ADUser ($ad.UserPrincipalName.Split('#')[0])
}
#### add AD Members to SG
Add-AdGroupMember -Identity $SGFB -Members $FB
# Looking to simplify, To be continued
I've written it the long way. Curious if anyone has ideas on how to do it better. Like I said, I'm a PS noob.

I think you could simplify your task by using a hash table ($map) where the Keys are the GUIDs of each Azure Group and the Values are each AD Group where the Az Group members need to be added.
For example:
$map = #{
'xxxxxxxxxxxx' = 'group 1', 'group 5', 'group 8' # Football
'zzzzzzzzzzzz' = 'group 2' # Volleyball
'yyyyyyyyyyyy' = 'group 3', 'group 4' # Boys Soccer
# and so on here
}
foreach($pair in $map.GetEnumerator()) {
# I think you could use `DisplayName` instead of `UserPrincipalName` here
# and don't have a need to parse the UPNs
$azMembers = (Get-AzureADGroupMember -ObjectId $pair.Key).DisplayName
foreach($adGroup in $pair.Value) {
Add-ADGroupMember -Identity $adGroup -Members $azMembers.DisplayName
}
}
As stated in the inline comment, I believe using .DisplayName would suffice since -Members takes one of these values:
Distinguished name
GUID (objectGUID)
Security identifier (objectSid)
SAM account name (sAMAccountName)
But considering this may not be case and that doesn't work, then an easier and safer way to parse the user's UserPrincipalName rather than using .Split('#')[0] would be to use the MailAddress class, so using it the code would look like this:
# here goes the `$map` too!
foreach($pair in $map.GetEnumerator()) {
$azMembers = [mailaddress[]] (Get-AzureADGroupMember -ObjectId $pair.Key).UserPrincipalName
foreach($adGroup in $pair.Value) {
Add-ADGroupMember -Identity $adGroup -Members $azMembers.User
}
}

Related

Query Multi-Domain for SamAccountName using PowerShell

I'm trying to populate an employee ID column in a CSV file by querying Active Directory against another column in the CSV file called "OwnerEmail" (Which is the user principal name). The problem is users in the owneremail column do not all belong to the same domain. How can I query 2 domains at one time?
Table for reference
Employee ID
OwnerEmail
DeptNumber
Costs
test#mail.com
0894
4654.45
test2#mail.com
3453
4994.15
This is what I have tried so far. The script isn't working and there are no error messages. Any Ideas
$Domains='us.domain.corp', 'uk.domain.corp'
$CSVImport |Select-Object #{
Name = "employeeID"
Expression = {
foreach($user in $CSVImport)
{
foreach($Domain in $Domains){
$user= (Get-ADUser -Filter "UserPrincipalName -eq '$($_.OwnerEmail)'" -Server $Domain -Properties 'SamAccountName').SamAccountName
}
}
}}, * |Select-Object employeeID, DepartmentNumber, OwnerEmail, #{Name="Costs"; Expression={"$ $($_.Cost)"}} | Export-Csv "$Env:temp/$OutputFile" -NoTypeInformation
How can I query 2 domains at one time?
There is no need to do this, you can query both at once with multithreading but seems like an overkill. What I would recommend is to query all users at once per Domain, the code below may seem awfully complicated but should be pretty efficient. See the inline comments for details.
# Import the Csv
$CSVImport = Import-Csv path\to\thecsv.csv
# Create a LDAP Filter to query all users at once
# This filter would look like this for example:
# (|(userPrincipalName=test#mail.com)(userPrincipalName=test2#mail.com))
$filter = "(|"
foreach($email in $CSVImport.OwnerEmail) {
if(-not [string]::IsNullOrWhiteSpace($email)) {
$filter += "(userPrincipalName=$email)"
}
}
$filter += ")"
# For each Domain, use the same filter and get all existing users
'us.domain.corp', 'uk.domain.corp' | ForEach-Object { $map = #{} } {
foreach($user in Get-ADUser -LDAPFilter $filter -Server $_) {
# and store them in a hashtable where
# the Keys are their `UserPrincipalName`
# and the Values are the attribute of interest (`SamAccountName`)
$map[$user.UserPrincipalName] = $user.SamAccountName
}
}
# Now we can simply use a calculated property with `Select-Object`
$CSVImport | Select-Object #{N='EmployeeID'; E={ $map[$_.OwnerEmail] }}, * |
Export-Csv "$Env:temp/$OutputFile" -NoTypeInformation

Add variable to Select-Object command in powershell and output to excel csv

I have a script that fetch user's manager info from a text file containing its respective groups and I want to put the results in an excel spreadsheet.
Also, I'd like to add a custom variable to the Select-Objects command so it shows up as another column in excel with each user's respective group
Here is what I have so far:
$Properties = #( 'Title', 'Department', 'Manager' )
[string[]]$arrayFromFile = Get-Content -Path 'C:\sslvpn-active.txt'
foreach($group in $arrayFromFile){
$searchb = "CN="+"$group"+",OU=SSLVPN,OU=UserGroupsRAS,DC=xi,DC=xxxinc,DC=net"
Get-ADGroupMember $searchb |
Get-ADUser -Properties $Properties |
Where-Object{ $_.Enabled } |
Select-Object Name, Title, Department,
#{Name = "ManagerName"; Expression = { (Get-ADUser $_.Manager).Name }},
#{Name = "ManagerMail"; Expression = { (Get-ADUser $_.Manager -Properties mail).Mail }}
}
The results are currently
SSLVPN-ABC
----------------------
Name : Joe Smith
Title : IT Engineer 2
Department : IT Support
ManagerName : Billy George
ManagerMail : Billy.George#xxx.com
Name : Matt Damon
Title : IT Engineer 3
Department : IT Support
ManagerName : Billy George
ManagerMail : Billy.George#xxx.com
SSLVPN-XYZ
----------------------
Name : Jen Loo
Title : Product Designer 3
Department : Product Design
ManagerName : Ben Smit
ManagerMail : Ben.Smit#xxx.com
...
I would like this result in excel format and also add a custom column called "Group" and have the group name (ex. SSLVPN-ABC) for each row. So the columns would be Group, Name, Title Department, ManagerName, ManagerMail and for the first group, the result cells will all be "SSLVPN-ABC".
Thanks
Change the select Statement like below.
Select-Object #{Name = "Group"; Expression = { $Group }},
Name, Title, Department,
#{Name = "ManagerName"; Expression = { (Get-ADUser $_.Manager).Name }},
#{Name = "ManagerMail"; Expression = { (Get-ADUser $_.Manager -Properties mail).Mail }}
Here you're just adding another calculated property that will reference the $Group iteration variable. Note the properties are listed in the order you asked for them.
If by some chance the $Group variable doesn't hold the name you can use (Get-ADGroup $searchb).Name in the expression.
Export to Csv as normal, I believe you already resolved that part...

Get Cosmosdb Container Collection items using powershell

Team, I had created new CosmosDB account in Azure portal with a container contains list of collection items. I am able to access Container details in power shell script.
How to list collection items or show specific collection item using partition key using power shell script
Power shell Script :
Get-AzResource -ResourceType "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers" -ApiVersion "2020-03-01" -ResourceGroupName "testRG" -Name "cosmosaccount1/database1/containercollection1"
You would need to use something like a thirdparty module for this. Azure Resource Manager doesnt support that, hence you need to talk to Cosmos DB directly.
https://github.com/PlagueHO/CosmosDB
The Cosmos DB repo has a set of examples to use Powershell: https://github.com/Azure/azure-cosmos-dotnet-v3/tree/master/Microsoft.Azure.Cosmos.Samples/Usage/PowerShellRestApi
Particularly to read Items: https://github.com/Azure/azure-cosmos-dotnet-v3/blob/master/Microsoft.Azure.Cosmos.Samples/Usage/PowerShellRestApi/PowerShellScripts/ReadItem.ps1
They are all using the REST API to do REST request, in this case, it is an authenticated GET to https://{databaseaccount}.documents.azure.com/dbs/{db-id}/colls/{coll-id}/docs/{id} (where databaseaccount is your account name, db-id is the id of your database, coll-id is the id of your collection/container, and id is your document id). It is also setting the x-ms-documentdb-partitionkey header for the partition key.
Like #4c74356b41 has indicated, you can use the CosmosDB module, which is now part of the official Az module.
Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force
You can see the available commands with Get-Commands:
Import-Module Az
Import-Module -Name CosmosDB
Get-Command -Module CosmosDB
Get all items in a collection
In order to get all entries inside a container, we use the Get-CosmosDbDocument command:
$subscription = "SubscriptionName"
$resourceGroupName = "ResourceGroupName"
$accountName = "AzureCosmosDBAccount"
$databaseName = "DatabaseName"
$cosmosContainer = "TargetCosmosDBContainer"
Set-AzContext $subscription
$backOffPolicy = New-CosmosDbBackoffPolicy -MaxRetries 5 -Method Additive -Delay 1000
$cosmosDbContext = New-CosmosDbContext -Account $accountName -Database
$databaseName -ResourceGroup $resourceGroupName -BackoffPolicy $backOffPolicy
$documentsPerRequest = 100
$continuationToken = $null
$documents = $null
do {
$responseHeader = $null
$getCosmosDbDocumentParameters = #{
Context = $cosmosDbContext
CollectionId = $cosmosContainer
MaxItemCount = $documentsPerRequest
ResponseHeader = ([ref] $responseHeader)
}
if ($continuationToken) {
$getCosmosDbDocumentParameters.ContinuationToken = $continuationToken
}
$documents += Get-CosmosDbDocument #getCosmosDbDocumentParameters
$continuationToken = Get-CosmosDbContinuationToken -ResponseHeader $responseHeader
} while (-not [System.String]::IsNullOrEmpty($continuationToken))
Note: There is no apparent limitation on the number of documents that can be retrieved with this command, but it stands to reason that the command will have the API limitation, and this is 4 MB (as documented here). The value here ($documentsPerRequest = 100) could prove to be either too big or too small, depending on the size of each document. I usually don't use this parameter, but I've mentioned it here in case someone needs it.
List specific collection item
To get a specific entry or group of entries from a container, we use the same Get-CosmosDbDocument command, in a slightly different way:
$query = "SELECT * FROM c WHERE c.property = 'propertyValue'"
$documents = Get-CosmosDbDocument -Context $cosmosDbContext -CollectionId $cosmosContainer -Query $query -QueryEnableCrossPartition $true
Note: For brevity I haven't went to the process of getting a continuation token, but if the query will return a result that is larger than 4 MB, then we will only receive the first part of the response. To make sure this does not happen we should add "Query" and "QueryEnableCrossPartition" in the $getCosmosDbDocumentParameters dictionary.

How to use loop powershell to assign multiple users to access an app group?

I want to add multiple users to my app group at a time, is there someone know how to user loop powershell to do that?
The Add-RdsAppGroupUser cmdlet as below can assigns a user to access the specified app group.
This cmdlet only takes in a single user principal name (UPN) at a time and only applies to users (not groups).
Add-RdsAppGroupUser -TenantName "contoso" -HostPoolName "contosoHostPool" -AppGroupName "Desktop Application Group" -UserPrincipalName "user1#contoso.com"
For example: I have 1000 users from user1#contoso.com to user1000#contoso.com, what should I write the code and how can i check the result after I finish it.
here's a demo of the idea. i don't have access to the cmdlet you use, so the loop shows its resulting parameter splat.
#region >>> fake getting a list of users
# in real life, use Get-Content or some other method
$UserList = #(
'One#contoso.com'
'Two#contoso.com'
'Three#contoso.com'
'Four#contoso.com'
'Five#contoso.com'
)
#endregion >>> fake getting a list of users
foreach ($UL_Item in $UserList)
{
# the following structure is called "Splatting"
# it puts some - or all - the parameters into a hashtable
# that can be fed to the cmdlet by replacing the "$" with an "#"
$ARAGU_Params = #{
TenantName = "contoso"
HostPoolName = "contosoHostPool"
AppGroupName = "Desktop Application Group"
UserPrincipalName = $UL_Item
}
#Add-RdsAppGroupUser #ARAGU_Params
# i don't have the above cmdlet, so this is just showing the parameters & values being passed to it
$ARAGU_Params
'=' * 30
}
output ...
Name Value
---- -----
HostPoolName contosoHostPool
UserPrincipalName One#contoso.com
TenantName contoso
AppGroupName Desktop Application Group
==============================
HostPoolName contosoHostPool
UserPrincipalName Two#contoso.com
TenantName contoso
AppGroupName Desktop Application Group
==============================
HostPoolName contosoHostPool
UserPrincipalName Three#contoso.com
TenantName contoso
AppGroupName Desktop Application Group
==============================
HostPoolName contosoHostPool
UserPrincipalName Four#contoso.com
TenantName contoso
AppGroupName Desktop Application Group
==============================
HostPoolName contosoHostPool
UserPrincipalName Five#contoso.com
TenantName contoso
AppGroupName Desktop Application Group
==============================

Powershell getting same values when using if in foreach

I'm trying to get metric from Azure to Zabbix.
The issue is that Metric for VM consists of 2 words:Percentage CPU, and Zabbix doesn't allow item keys to consists of 2 words. I also tried Percentage%20CPU but getting errors in Zabbix, and I created Zabbix key percentage_cpu.
So I decided prior sending data from Zabbix to Azure to "translate" percentage_cpu to Percentage%20CPU. This works great if only that key is present, but issue starts when I add another key (in this example SQL metric).
For SQL metric all values are in one word - no need to change anything, but then metric for VM is also assigned to SQL. I'm trying to avoid writing separate file for every service
$host_items = Get-ZabbixHostItems -url $zabbix_url -auth $zabbix_auth -
zabbix_host $host_name
foreach ($host_item in $host_items)
{
#$host_item_details = select-string -InputObject $host_item.key_ -Pattern '^(azure\.sql)\.(.*)\.(.*)\[\"(.*)\"\]$';
$host_item_details = select-string -InputObject $host_item.key_ -Pattern '^(azure\.\w{2,})\.(.*)\.(.*)\[\"(.*)\"\,(.*)]$';
#$host_item_details = select-string -InputObject $host_item.key_ -Pattern '^(azure)\.(.*)\.(.*)\.(.*)\[\"(.*)\"\,(.*)]$';
$host_item_provider = $host_item_details.Matches.groups[1];
$host_item_metric = $host_item_details.Matches.groups[2];
$host_item_timegrain = $host_item_details.Matches.groups[3];
$host_item_resource = $host_item_details.Matches.groups[4];
$host_item_suffix = $host_item_details.Matches.groups[5];
if ($host_item_metric='percentage_cpu')
{$host_item_metric='Percentage%20CPU'}
else
{ $host_item_metric = $host_item_details.Matches.groups[2];}
#}
$uri = "https://management.azure.com{0}/providers/microsoft.insights/metrics?api-version={1}&interval={2}&timespan={3}&metric={4}" -f `
$host_item_resource, `
"2017-05-01-preview", `
$host_item_timegrain.ToString().ToUpper(), `
$($(get-date).ToUniversalTime().addminutes(-15).tostring("yyyy-MM-ddTHH:mm:ssZ") + "/" + $(get-date).ToUniversalTime().addminutes(-2).tostring("yyyy-MM-ddTHH:mm:ssZ")), `
$host_item_metric;
write-host $uri;
}
output of hostitems_
azure.sql.dtu_consumption_percent.pt1m["/subscriptions/111-222/resourceGroups/rg/providers/Microsoft.Sql/servers/mojsql/databases/test",common]
azure.vm.percentage_cpu.pt1m["/subscriptions/111-222/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/test",common]
When I ran code above I'm getting these URI's
https://management.azure.com/subscriptions/111-222/resourceGroups/rg/providers/Microsoft.Sql/servers/mojsql/databases/test/providers/microsoft.insights/m
etrics?api-version=2017-05-01-preview&interval=PT1M&timespan=2018-08-11T07:38:05Z/2018-08-11T07:51:05Z&metric=Percentage%20CPU
https://management.azure.com/subscriptions/111-222/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/test/providers/microsoft.insights/metric
s?api-version=2017-05-01-preview&interval=PT1M&timespan=2018-08-11T07:38:05Z/2018-08-11T07:51:05Z&metric=Percentage%20CPU
For first link (SQL) metric should be dtu_consumption but I'm getting same metric for both links
Second attempt:
if ($host_item_metric -eq 'percentage_cpu')
{$host_item_metric='Percentage%20CPU';}
else
{ $host_item_metric = $host_item_details.Matches.groups[2];}
write-host $host_item_metric
}
output: (original values)
dtu_consumption_percent
percentage_cpu
had to use -like
if ($host_item_metric -like 'percentage_cpu')
{$host_item_metric='Percentage%20CPU';}
else
{ $host_item_metric = $host_item_details.Matches.groups[2]}

Resources