Can someone help define how I use New-MgRoleManagementDirectoryRoleAssignment?
The MS doc is, well lets say a little confusing!
Essentially I want to assign the application administrator role to a registered application. Is this even the correct cmdlet?
Thanks
/A
There's some more detail about this in the API documentation
Parameter
Type
Description
roleDefinitionId
String
Identifier of the role definition the assignment is for.
principalId
String
The identifier of the principal to which the assignment is granted.
directoryScopeId
String
Identifier of the directory object representing the scope of the assignment. Either this property or appScopeId is required. The scope of an assignment determines the set of resources for which the principal has been granted access. Directory scopes are shared scopes stored in the directory that are understood by multiple applications. Use / for tenant-wide scope. Use appScopeId to limit the scope to an application only.
appScopeId
String
Identifier of the app-specific scope when the assignment scope is app-specific. Either this property or directoryScopeId is required. App scopes are scopes that are defined and understood by this application only. Use / for tenant-wide app scopes. Use directoryScopeId to limit the scope to particular directory objects, for example, administrative units.
Example to assign Global Administrator role to an Enterprise Application
$roleDefinitionId = (Get-MgRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq 'Global Administrator'").Id
$appObjectId = (Get-MgServicePrincipal -Filter "DisplayName eq 'AppName'").Id
New-MgRoleManagementDirectoryRoleAssignment -PrincipalId $appObjectId -RoleDefinitionId $roleDefinitionId -DirectoryScopeId "/"
Note
From your use-case, you can also use the cmdlet New-MgDirectoryRoleMemberByRef
# Get the role Id
$roleId = (Get-MgDirectoryRole -Filter "DisplayName eq 'Global Administrator'").Id
# Get the object ID of your Enterprise Application
$appObjectId = (Get-MgServicePrincipal -Filter "DisplayName eq 'AppName'").Id
$body = #{
"#odata.id"= "https://graph.microsoft.com/v1.0/directoryObjects/{$($appObjectId)}"
}
New-MgDirectoryRoleMemberByRef -DirectoryRoleId $roleId -BodyParameter $body
Related
I'm building a policy assignment that needs to exclude specific resource group according to their name. Resource groups starting with "Atlanta" should be excluded.
An array would not suffice. Is creating a parameter or variable with wildcard "Atlanta*" possible or how do I achieve this?
I don't know of a way to do this in the portal. I think your best option though not ideal is to use PowerShell/CLI to programmatically assign this policy to the RG's you wish. The logic inside your PowerShell/CLI can exclude the "Atlanta" RG's.
# Get a reference to the resource group that is the scope of the assignment
$rg = Get-AzResourceGroup -Name '<resourceGroupName>'
# Get a reference to the built-in policy definition to assign
$definition = Get-AzPolicyDefinition | Where-Object { $_.Properties.DisplayName -eq 'Audit VMs that do not use managed disks' }
# Create the policy assignment with the built-in definition against your resource group
New-AzPolicyAssignment -Name 'audit-vm-manageddisks' -DisplayName 'Audit VMs without managed disks Assignment' -Scope $rg.ResourceId -PolicyDefinition $definition
https://learn.microsoft.com/en-us/azure/governance/policy/assign-policy-powershell
I'm trying to execute the Invoke-Sqlcmd command (from the SqlServer module) to run a query as a different AD user. I know there's the -Credential argument, but that doesn't seem to work.
Thus, I thought using Start-Job might be an option, as shown in the snippet below.
$username = 'dummy_domain\dummy_user'
$userpassword = 'dummy_pwd' | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential ($username, $password)
$job = Start-Job -ScriptBlock {Import-Module SqlServer; Invoke-Sqlcmd -query "exec sp_who" -ServerInstance 'dummy_mssql_server' -As DataSet} -Credential $credential
$data = Receive-Job -Job $job -Wait -AutoRemoveJob
However, when looking at the variable type that the job returned, it isn't what I expected.
> $data.GetType().FullName
System.Management.Automation.PSObject
> $data.Tables[0].GetType().FullName
System.Collections.ArrayList
If I run the code in the ScriptBlock directly, these are the variable types that PS returns:
> $data.GetType().FullName
System.Data.DataSet
> $data.Tables[0].GetType().FullName
System.Data.DataTable
I tried casting the $data variable to [System.Data.DataSet], which resulted in the following error message:
Cannot convert value "System.Data.DataSet" to type "System.Data.DataSet".
Error: "Cannot convert the "System.Data.DataSet" value of type
"Deserialized.System.Data.DataSet" to type "System.Data.DataSet"."
Questions:
Is there a better way to run SQL queries under a different AD account, using the Invoke-Sqlcmd command?
Is there a way to get the correct/expected variable type to be returned when calling Receive-Job?
Update
When I run $data.Tables | Get-Member, one of the properties returned is:
Tables Property Deserialized.System.Data.DataTableCollection {get;set;}
Is there a way to get the correct/expected variable type to be returned when calling Receive-Job?
Due to using a background job, you lose type fidelity: the objects you're getting back are method-less emulations of the original types.
Manually recreating the original types is not worth the effort and may not even be possible - though perhaps working with the emulations is enough.
Update: As per your own answer, switching from working with System.DataSet to System.DataTable resulted in serviceable emulations for you.[1]
See the bottom section for more information.
Is there a better way to run SQL queries under a different AD account, using the Invoke-Sqlcmd command?
You need an in-process invocation method in order to maintain type fidelity, but I don't think that is possible with arbitrary commands if you want to impersonate another user.
For instance, the in-process (thread-based) alternative to Start-Job - Start-ThreadJob - doesn't have a -Credential parameter.
Your best bet is therefore to try to make Invoke-SqlCmd's -Credential parameter work for you or find a different in-process way of running your queries with a given user's credentials.
Serialization and deserialization of objects in background jobs / remoting / mini-shells:
Whenever PowerShell marshals objects across process boundaries, it employs XML-based serialization at the source, and deserialization at the destination, using a format known as CLI XML (Common Language Infrastructure XML).
This happens in the context of PowerShell remoting (e.g., Invoke-Command calls with the
-ComputerName parameter) as well as in background jobs (Start-Job) and so-called mini-shells (which are implicitly used when you call the PowerShell CLI from inside PowerShell itself with a script block; e.g., powershell.exe { Get-Item / }).
This deserialization maintains type fidelity only for a limited set of known types, as specified in MS-PSRP, the PowerShell Remoting Protocol Specification. That is, only instances of a fixed set of types are deserialized as their original type.
Instances of all other types are emulated: list-like types become [System.Collections.ArrayList] instances, dictionary types become [hasthable] instances, and other types become method-less (properties-only) custom objects ([pscustomobject] instances), whose .pstypenames property contains the original type name prefixed with Deserialized. (e.g., Deserialized.System.Data.DataTable), as well as the equally prefixed names of the type's base types (inheritance hierarchy).
Additionally, the recursion depth for object graphs of non-[pscustomobject] instances is limited to 1 level - note that this includes instance of PowerShell custom classes, created with the class keyword: That is, if an input object's property values aren't instance of well-known types themselves (the latter includes single-value-only types, including .NET primitive types such as [int], as opposed to types composed of multiple properties), they are replaced by their .ToString() representations (e.g., type System.IO.DirectoryInfo has a .Parent property that is another System.IO.DirectoryInfo instance, which means that the .Parent property value serializes as the .ToString() representation of that instance, which is its full path string); in short: Non-custom (scalar) objects serialize such that property values that aren't themselves instances of well-known types are replaced by their .ToString() representation; see this answer for a concrete example.
By contrast, explicit use of CLI XML serialization via Export-Clixml defaults to a depth of 2 (you can specify a custom depth via -Depth and you can similarly control the depth if you use the underlying System.Management.Automation.PSSerializer type directly).
Depending on the original type, you may be able to reconstruct instances of the original type manually, but that is not guaranteed.
(You can get the original type's full name by calling .pstypenames[0] -replace '^Deserialized\.' on a given custom object.)
Depending on your processing needs, however, the emulations of the original objects may be sufficient.
[1] Using System.DataTable results in usable emulated objects, because you get a System.Collections.ArrayList instance that emulates the table, and custom objects with the original property values for its System.DataRow instances. The reason this works is that PowerShell has built-in logic to treat System.DataTable implicitly as an array of its data rows, whereas the same doesn't apply to System.DataSet.
I can't say for question 2 as I've never used the job commands but when it comes to running the Invoke-Sqlcmd I always make sure that the account that runs the script has the correct access to run the SQL.
The plus to this is that you don't need to store the credentials inside the script, but is usually a moot point as the scripts are stored out of reach of most folks, although some bosses can be nit picky!
Out of curiosity how do the results compare if you pipe them to Get-Member?
For those interested, below is the code I implemented. Depending on whether or not $credential is passed, Invoke-Sqlcmd will either run directly, or using a background job.
I had to use -As DataTables instead of -As DataSet, as the latter seems to have issues with serialisation/deserialisation (see accepted answer for more info).
function Exec-SQL($server, $database, $query, $credential) {
$sqlData = #()
$scriptBlock = {
Param($params)
Import-Module SqlServer
return Invoke-Sqlcmd -ServerInstance $params.server -Database $params.database -query $params.query -As DataTables -OutputSqlErrors $true
}
if ($PSBoundParameters.ContainsKey("credential")) {
$job = Start-Job -ScriptBlock $scriptBlock -Credential $credential -ArgumentList $PSBoundParameters
$sqlData = Receive-Job -Job $job -Wait -AutoRemoveJob
} else {
$sqlData = & $scriptBlock -params $PSBoundParameters
}
return $sqlData
}
I am looking for a way to concatenate a string and put it in one active directory user account object, to be precise, in altsecurityidentities.
The value to be input will be as following:
" which is constant, and (firstName)(whitespace)(lastname) (custom value which can be taken from another attribute, which is in form x.yyyyyyyy.z (what matters to me is yyyyyyy part (.substring(2,8)) works like charm here.
I'd like to do it for several accounts that are listed in variable of type TypeName: Microsoft.ActiveDirectory.Management.ADUser.
So that it can be set for all accounts under $accounts variable.
So far I have the code to create the attribute value for one account listed in there:
$accounts | %{'constant value'+$.givenname+' '+$.surname+' '+'('+$(($_.attributename).substring(2,8))+')'}
This, i'd like to put in altsecurityidentities attribute value, log to event viewer success and errors
You're almost there really just need to apply the value to the desired field:
$accounts | ForEach-Object { Set-ADUser -Identity $_ -Add #{altsecurityidentities = "constant value $($_.givenname) $($_.surname) ($($_.attributename.substring(2,8)))"} }
I have tidied up your code by embedding the variables in a string rather than using concatenation, which is much cleaner.
I'm preparing for a move to office365 and since we have the mydomain.local domain I need to add an alternative UPN (same as my SMTP namespace) so mydomain.com. I added the alternate UPN to my domain and now I want to change multiple users UPN at once.
I select multiple users > right click > properties > account > UPN suffix and select the UPN from the drop-down. When that's done I click OK or Apply and I get following error for all selected user:
The specified directory service attribute or value does not exist.
When I change it from one user it works without a problem.
My question now is, can someone help me solve tell me why this error is showing or what way I can achieve this.
Thanks
You can try http://admodify.codeplex.com/.
There is an article showing an example of its uage here: http://blogs.technet.com/exchange/archive/2004/08/04/208045.aspx
Use the following powershell scripts. Change "contoso.local" to your actual domain name.
$localUsers = Get-ADUser -Filter {UserPrincipalName -like "contoso.local"} -Properties UserPrincipalName -ResultSetSize $null
$localUsers | foreach { $newUpn = $_.UserPrincipalName.Replace("contoso.local", "yourdomain.com"; $_ | Set-ADUser -UserPrincipalName $newUpn}
It is best to use a script to change bulk users rather than using the method you mentioned.
You can use either a PowerShell script (recommended) or a VBScript for this.
PowerShell script (using a CSV file):
http://gallery.technet.microsoft.com/Change-UPN-592177ea
PowerShell script (for all users in an OU searchbase):
http://community.spiceworks.com/scripts/show/1457-mass-change-upn-suffix
VBScript:
http://blogs.technet.com/b/heyscriptingguy/archive/2004/12/06/how-can-i-assign-a-new-upn-to-all-my-users.aspx?Redirected=true
I have the DN of a user in Active Directory, I want to get the "NT style" domain\user from this. The sAMAccountname AD property gives me the user part, but what about the domain?
Thanks
You can get it by taking the last part of the user DN (DC=domain,DC=local) and adding CN=Partitions,CN=Configuration, before.
Then do a subtree search for (&(nCName="DC=domain,DC=local")(nETBIOSName=*)) with CN=Partitions, CN=Configuration, DC=domain, DC=local as the starting point; the entry you get back will have the NETBIOS name of the domain in the nETBIOSName-attribute.
How about using --> System.Security.Principal.NTAccount.ToString()
See msdn info about it here: NTAccount.ToString()
This should return a string in the format of domain\user... is this what you are after?
The easiest way to do this conversion is through the DsCrackNames API. You specify the input format and output format and it does the conversion for you.
Here is the PowerShell code:
$hash = #{} //this contains the map of CN and nCNAME
$Filter = '(nETBIOSName=*)'
$RootOU = "CN=Partitions,CN=Configuration,DC=DOMAIN,DC=LOCAL" //Change this to your org's domain
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.SearchScope = "subtree"
$Searcher.Filter = $Filter
$Searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$($RootOU)")
$Searcher.FindAll()|sort | foreach { $hash[($_.Properties.ncname).Trim()] = ($_.Properties.cn).Trim() }
$hash.GetEnumerator() | sort -Property Value
If the user details are available in $userDetails, then the you can get the correct domain with this:
$hash[[regex]::Match($userDetails.DistinguishedName, 'DC=.*').Value]
and the final username would look like this:
$hash[[regex]::Match($userDetails.DistinguishedName, 'DC=.*').Value] + "\" + $userDetails.SamAccountName