SetAccessRule failing while in script, works otherwise - windows

I'm trying to figure our why a call to Set-Acl in powershell failes.
The call is part of a larger script used in setting up an AD OU. The basic steps of that scripts are:
Create a new OU
Add a group to that OU
Create a folder on a file share.
Add permissions for the newly created group to that newly created folder.
Copy the content of a template folder to the newly folder.
Create a DFS folder to pointing to the new folder.
The script works fine except for a call to SetACL:
#Some initialization data. In reality, the exact values are generated using the script parameters.
$NewDataPath="\\server\share\folder\"
$NewGroupName="UserGroup_1234"
#The actual code from the original script
$Acl = Get-Acl $NewDataPath
$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($NewGroupName, "Modify", "ContainerInherit,ObjectInherit", "None", "Allow")
#exception happens on the following line
$Acl.SetAccessRule($Ar)
#whetever happens afterward works fine
Set-Acl -Path $NewDataPath $Acl
This raises the following exception:
Exception calling "SetAccessRule" with "1" argument(s): "Some or all
identity references could not be translated."
+ $Acl.SetAccessRule <<<< ($Ar)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
The really weird thing is that if I run each line manually, it works. I have moved the code to a separate function and calling it manually after the initial code fails (with the exact same parameter) works as well.
Even weirder: if I move the section of code that update the permissions AFTER I have copied data into it, it works.
It is as if the SetAccessRule method call (or perhaps the FileSystemAccessRule object) wasn't able to access the new data in AD, even though all other calls works fine and if a slight delay is introduce, it recovers.
While I was able to find a workaround, I'd really would like to understand what is happening here.

This seems to be a typical race-like condition when creating multiple new AD objects that depend on each other.
I haven't confirmed this, but I suspect that when you supply an account name as a string to the FileSystemAccessRule constructor, .NET uses the LSA Lookup Cache on your machine to resolve the name to a security identifier.
Since you literally just created the account on another machine (the DC) a few hundred milliseconds ago, the lookup cache fails to translate the name.
You can easily circumvent this by supplying the Security Identifier of the new account instead.
To get the SID, specify the -PassThru switch when you create the group, this will return the new object (including it's SID):
$NewGroupInstance = New-ADGroup -Name $NewGroupName -PassThru # -Path and so forth
# ...
$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($NewGroupInstance.SID, "Modify", "ContainerInherit,ObjectInherit", "None", "Allow")

Related

Powershell and IBM/Lotus Domino

I have written 9 routines in the last few weeks in Powershell for Domino, they all appear to do what I need except 1!
This is the RegisterNewUser method, which does everything except the Email address. If I setup a user via Domino Administrator, I get everything including the email address ie internal address that is 'bob patz/smallhome'.
If I use my code this uses the registration process but all I end up with is the domain part of the internal email address '#smallhome'.
Does anyone know how to correct this? I don't think powershell uses the #formula language in any form, so I assume i somehow need to find the right column in a document or database and append the fullname in there somehow.
Is there anyone out there who can help in anyway?
regards
Mark
<#TOP TIP: THIS MUST RUN UNDER POWERSHELL (X86), ELSE IT WILL NOT WORK!!
This Powershell Function was created in March 2020 by (myself) Mark Baker as 'a' just to see if I can de-mystify
some of the Domino Database stucture, after running short bits of code and using Get-Member on some parts
of it and looking at online code snippets and reading some of the online info from IBM I have come up with
the function below to Create a New Lotus Notes User.
#
Original Code: 31/03/2020 by MBaker
A lot of work testing and diagnosing the different settings and values EVENTUALLY lead me to getting this working,
as at 08/04/2020 I just need to work out the settings for setting the correct email address per person.
#
This is how to use this function:
New-DominoUserRegistration "hazell" "C:\Program Files\IBM\Lotus\Notes\Data\ids\people\dhazell.id" "CN=Dom-01/O=Smallhome"
"Daniel" "" "swindon" "Work" "comment" "mail\dhazell" " " "password" 176
"dhazell"
Main use of this function is to connect to an IBM Domino Server and Create a New lotus notes user.
>
Function New-DominoUserRegistration {
[cmdletbinding()]
param (
[parameter(Position=0,Mandatory=$true,ValueFromPipeline=$True)][string]$lastname,
[parameter(Position=1,Mandatory=$true,ValueFromPipeline=$True)][string]$Useridfile,
[parameter(Position=2,Mandatory=$true,ValueFromPipeline=$True)][string]$mailserver,
[parameter(Position=3,Mandatory=$true,ValueFromPipeline=$True)][string]$firstname,
[parameter(Position=4,Mandatory=$false,ValueFromPipeline=$True)][string]$middle,
[parameter(Position=5,Mandatory=$true,ValueFromPipeline=$True)][string]$certpw,
[parameter(Position=6,Mandatory=$true,ValueFromPipeline=$True)][string]$location,
[parameter(Position=7,Mandatory=$true,ValueFromPipeline=$True)][string]$comment,
[parameter(Position=8,Mandatory=$true,ValueFromPipeline=$True)][string]$maildbpath,
[parameter(Position=9,Mandatory=$true,ValueFromPipeline=$True)][string]$fwddomain,
[parameter(Position=10,Mandatory=$true,ValueFromPipeline=$True)][string]$userpw,
[parameter(Position=11,Mandatory=$true,ValueFromPipeline=$True)][int]$usertype,
[parameter(Position=12,Mandatory=$true,ValueFromPipeline=$True)][string]$ShortName
)
cls
# Create Lotus Notes Object
$DomSession = New-Object -ComObject Lotus.NotesSession
# Initialize Lotus Notes Object
# "It'll use your open notes session and authentication Details"
$DomSession.Initialize()
# Use Method from Objects returned in variable $domsession one of which is CreateAdministrationProcess which
# takes a Server as input
$adminProcess = $Domsession.CreateRegistration()
$expiration = (Get-Date).adddays(1095)
$adminprocess.certifieridfile="C:\Program Files (x86)\IBM\Lotus\Notes\Data\ids\cert.id"
$adminprocess.Expiration =$expiration
#$adminprocess.RegistrationLog ="C:\program files (x86)\IBM\lotus\notes\data\reglog.nsf"
#[int]$adminProcess.MinPasswordLength=5
$adminprocess.RegistrationServer="Dom-01/smallhome"
$adminprocess.UpdateAddressBook=$true
$adminProcess.GroupList="Test4"
#$adminProcess.CreateMailDb=$true
#[int]$adminProcess.MailQuotaSizeLimit="100"
#[int]$adminProcess.MailQuotaWarningThreshold="90"
$adminProcess.PolicyName="/Registration_Settings"
$adminProcess.ShortName=$ShortName
[int]$adminProcess.MailOwnerAccess=2
$adminProcess.MailACLManager="LocalDomainAdmins"
$adminProcess.MailInternetAddress="$ShortName"+"#smallhome.local"
$adminProcess.MailTemplateName="Mail85.ntf"
$Notesid=$adminprocess.RegisterNewUser($lastname,$Useridfile,$mailserver,$firstname,$middle,$certpw,$location,$comment,$maildbpath,$fwddomain,$userpw,$usertype)
}
New-DominoUserRegistration "archer" "C:\Program Files (x86)\IBM\Lotus\Notes\Data\ids\people\barcher.id" "CN=Dom-01/O=Smallhome" "basil" "" "swindon" "Work" "comment" "mail\barcher" " " "password" 176 "barcher"
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/2Wamq.png
I now have an answer for this, this came from someone else on another board: the line [parameter(Position=9,Mandatory=$true,ValueFromPipeline=$True)][string]$fwddomain,
Needed to have [AllowEmptyString()] inserted before [string]$fwddomain, and the input field was " " and needed to be "". It appears the $fwddomain has an impact on the internal email address name ie: 'bob patz/smallhome'.
I have tested this and it shows up in the people view and also I can now send emails to other users and ones self!
Problem solved.
Regards
Mark

Show inputs for Geolocation on Win10

I'm troubleshooting Win10 PCs getting bad location information. The Windows Geolocation service puts them in Colorado when they should be in California. That changes the time zone and they miss meetings.
Anyway, I'm looking for the data that is being fed into the Geolocation service to see what could be causing the bad results. I think that the info will be in the Position source. The doc is here.
And I would like to be able to access in the object in PowerShell for troubleshooting.
I can do something similar with the geolocation output which will show me the longitude and latitude like this:
Add-Type -AssemblyName System.Device
$GeoWatcher = New-Object System.Device.Location.GeoCoordinateWatcher
$GeoWatcher.Start()
Can anyone "translate" how to load the correct assemblies for the Positionsource?
Note, if you are doing this on office locked user, meaning, desktops/laptops/mobile devices, then it's going to report the location based on the egress gateway / external IPA, not the actual host location.
Yet, this sounds like a possible duplicate of this answer:
Powershell: Getting GPS Coordinates in Windows 10 - Using Windows Location API?
# Required to access System.Device.Location namespace
Add-Type -AssemblyName System.Device
# Create the required object
$GeoWatcher = New-Object System.Device.Location.GeoCoordinateWatcher
# Begin resolving current locaton
$GeoWatcher.Start()
while (($GeoWatcher.Status -ne 'Ready') -and ($GeoWatcher.Permission -ne 'Denied'))
{
#Wait for discovery.
Start-Sleep -Milliseconds 100
}
if ($GeoWatcher.Permission -eq 'Denied')
{Write-Error 'Access Denied for Location Information'}
else
{
#Select the relevent results.
$GeoWatcher.Position.Location |
Select Latitude,Longitude
}
Yet, see the MS doc here:
PositionSource Enum
Indicates the source used to obtain a Geocoordinate.
Definition
Namespace : Windows.Devices.Geolocation
Assemblies : Windows.Devices.Geolocation.dll, Windows.dll
Geocoordinate Class
Contains the information for identifying a geographic location.
Definition
Namespace : Windows.Devices.Geolocation
Assemblies : Windows.Devices.Geolocation.dll, Windows.dll

Issues in installing COM + Component using PowerShell script

I'm trying to install COM + Component using powershell. But I'm getting following error.
Unable to find type [some.dll]. Make sure that the assembly that contains this
type is loaded.
+ $comAdmin.InstallComponent("test", [some.dll]);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (COMITSServer.dll:TypeName) [], Runtime
Exception
+ FullyQualifiedErrorId : TypeNotFound
and here is my powershell script:
Install COM + Components
$comAdmin = New-Object -comobject COMAdmin.COMAdminCatalog;
$comAdmin.InstallComponent("some", [some.dll]);
#if an exception occurs in installing COM+ then display the message below
if (!$?)
{
Write-Host "Unable to Install the COM+ Component. Aborting..."
exit -1
}
My powershell version is 4.0
Can someone please help me on this.
Thank you.
Square brackets in PowerShell indicate a type, like [string] or [int32] or [System.Array] or [System.Math]. The error message is complaining because you're telling PowerShell that COMITSServer.dll is a registered and loaded data type.
Additionally, the InstallComponent method of COMAdminCatalog appears to me to have four arguments, not two. You should be able to confirm that by looking at the definition, but I don't know if v2.0 supports doing it like this:
PS U:\> $comAdmin = New-Object -comobject COMAdmin.COMAdminCatalog
PS U:\> $comAdmin | gm | where { $_.Name -eq 'InstallComponent' }
TypeName: System.__ComObject#{790c6e0b-9194-4cc9-9426-a48a63185696}
Name MemberType Definition
---- ---------- ----------
InstallComponent Method void InstallComponent (string, string, string, string)
As such, I would try this:
$comAdmin.InstallComponent("ITSServerOO2", "COMITSServer.dll", "", "");
That appears to be how the VB code here calls the same method:
' Open a session with the catalog.
' Instantiate a COMAdminCatalog object.
Dim objCatalog As COMAdminCatalog
Set objCatalog = CreateObject("COMAdmin.COMAdminCatalog")
[...]
' Install components into the application.
' Use the InstallComponent method on COMAdminCatalog.
' In this case, the last two parameters are passed as empty strings.
objCatalog.InstallComponent "MyHomeZoo","MyZoo.DLL","",""
Here is the class definition of that function, I believe, athough I'm not familiar enough with COM+ to know if COMAdminCatalog and ICOMAdminCatalog are the same.
This is an answer to the question about the 0x80110401 HRESULT. (BTW, follow up questions should be answered in a new post on SO, not in the comments of an existing question). This allows other people to find answers when they have the same question.
Documentation for ICOMAdminCatalog::InstallComponent. As the documentation explains the first parameter is GUID, the second is the DLL being registered. The third parameter (typelib) and fourth (proxy stub DLL) are optional and can be specified as an empty string.
"test" is not a valid GUID. Note that a GUID is a distinct .NET type (System.Guid). However the documentation calls for a BSTR which would translate into a System.String. To get a new GUID as a string use this: [Guid]::NewGuid().toString().
Note that GUIDs for COM+ components are "well known" values that are used by both the COM servers implementing an interface(s) and by clients that are consuming the interface(s) from a COM server. So normally you wouldn't want to generate a new GUID when registering a COM server, but use the one the developer created when the COM server was developed. However if you don't know what the correct GUID to use is, then generating a new GUID will at least allow you to make progress in developing your script.
This may or may not fix the problem causing the 0x80110401, but it will definitely fix a problem you'll run into sooner or later.

Adding members to security group: SetInfo returns error 80070005

Background
I working on (modifying) a vbscript intended to create an AD shadow group formed by all users of several OU's.
The script logs into a remote AD, using alternate credentials.Note: The user I log in as, have full AD read access, but only inherited write access to the OU containing the group I'm adding members to.
Problematic code
The script is easily able to log in, scan for users on the OU's, open the shadow group and list it's current members, perform compares and locate the users to add and remove....but...
the actual adding (and removing) of members to the named security group fails, Error: 80070005 Srce: Active Directory Desc: Access is denied., when I apply .SetInfo.
I'm using this code to read and write:
aMembers = oGroup.GetEx("member") ' This gets populated as it should.
oGroup.PutEx ADS_PROPERTY_APPEND, "member", Array("CN=Doe\, John [LOCATION/COUNTRY],OU=foo,OU=bar,OU=Country,DC=domain,DC=org")
oGroup.SetInfo ' This fails
Just in case this is related to how I log into the AD, here's the code:
' Open an ADO connection using full credentials
Set oConnection = CreateObject("ADODB.Connection")
oConnection.Provider = "ADsDSOObject"
oConnection.Properties("User ID") = sLDAP_USER ' "domain\user"
oConnection.Properties("Password") = sLDAP_PASS ' "pass!word"
oConnection.Properties("Encrypt Password") = True
oConnection.Properties("ADSI Flag") = ADS_SECURE_AUTHENTICATION Or ADS_SERVER_BIND
oConnection.Open "Active Directory Provider"
Verifying my login users rights
I've seen lots of similar posts with this sort of error, and have only seen answers stating that this is a permission problem - however never seen a description of what permission was missing/wrong. The Access is denied error really made me doubt that the login I used had the right permissions, so I attempted to add members to the group by using JXplore. It works, but only if I enable the option to ignore schema checking (Based on online advice, when using JXplore with AD instead of pure LDAP).
Edit: Without ignoring the schema checking, the following properties are also requested to be populated: instanceType, nTSecurityDescriptor and objectCatagory, but I notice they are blank on existing users.
Questions
Did I fall into an obvious pitfall?
Do I also somehow need to ignore the schema in my script? If so, how to do that?

How to fetch registry path from EventArrivedEventArgs object

I try to watch the registry events via WMI. I use the below query to watch any events inside HKLM\softwares
WqlEventQuery query = new WqlEventQuery(
"SELECT * FROM RegistryTreeChangeEvent WHERE " +
"(Hive = 'HKEY_LOCAL_MACHINE')" +
"AND Rootpath = 'Software'"
As expected it catches all events in EventArrivedEventArgs.
example: 1) if there is a newkey inside Hklm\software\microsoft, it captures
2) if there is a value change inside Hklm\software\microsoft\windows, it captures
However I need to know the registry path or key or value in which change has occured.
I dont know how to interpret the EventArrivedEventArgs object to get it.
Can anyone help me.
I don't believe this is possible. EventArrivedEventArgs will return an instance of RegistryTreeChangeEvent and the only thing you know about the event is the root path you are monitoring. You can work around this using the RegistryKeyChangeEvent class, specifying more than one key in the query Where clause. For example (not tested):
SELECT * FROM RegistryKeyChangeEvent
WHERE Hive='HKEY_LOCAL_MACHINE' AND
(KeyPath='SOFTWARE\Microsoft' OR
KeyPath='SOFTWARE\Microsoft\Windows')
In this case you would use EventArrivedEventArgs.NewEvent property to get the RegistryKeyChangeEvent instance and its Keypath property to get the registry key that was changed.
After the analysis, Its clear that Key path for subkeys couldnot be obtained through registry events. Because Regkeychangeevent could not monitor subkeys and reg treechange event monitors subkeys which would not give the key path, the change has occured. Hence preimage post image's diff should be the only solution so far.

Resources