2

I am trying to drop users from a SQL server database via PowerShell using the following code:

$sql_server = "mysqlserver"
$dbname = "mydb"

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null

$server = New-Object ("Microsoft.SqlServer.Management.Smo.Server")($sql_server)
$db = $server.Databases["$dbname"]
$logins = $db.users

if ($logins.count -gt 0) {
    foreach ($login in $logins) {
        if ($login -notmatch "\[dbo\]|\[guest\]|\[INFORMATION_SCHEMA\]|\[sys\]") {
            $user = $login -replace '[^a-zA-Z\.\\]'
            write-host "Dropping $user"
            $db.Users[$user].Drop();
        }
    }
}

However I am getting an error stating:

Collection was modified; enumeration operation may not execute. + foreach ($login in $logins) { + ~~~~~~ + CategoryInfo : OperationStopped: (:) [], InvalidOperationException + FullyQualifiedErrorId : System.InvalidOperationException

I think its saying that $logins is being changed because I'm deleting users? I'm not sure how to work around that though?


EDIT


The following should work. Thanks to @Neil Hibbert for the help.

$sql_server = "mysqlserver"
$dbname = "mydb"

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null

$server = New-Object ("Microsoft.SqlServer.Management.Smo.Server")($sql_server)
$db = $server.Databases["$dbname"]
$logins = $db.users

if ($logins.count -gt 0) {
    for ($i=0; $i -lt $logins.count; $i++) {
        if ($logins[$i] -notmatch "\[dbo\]|\[guest\]|\[INFORMATION_SCHEMA\]|\[sys\]") {
            $user = $logins[$i] -replace '[^a-zA-Z\.\\]'
            write-host "Dropping $user"
            $db.Users[$user].Drop()
        }
    }
}
7
  • 1
    I think this is more of a .NET thing. Whilst iterating over collections with a foreach-loop, individual elements may not be modified, which is what you are trying to do. If you replaced the foreach loop with a for-loop and tried the same action, it should work out successful. Commented Jun 15, 2017 at 12:49
  • Not sure how I can change this to a for loop? I tried for ($i=0; $i -lt $logins.count; $i++) { if ($login[$i] -notmatch "[dbo]|[guest]|[INFORMATION_SCHEMA]|[sys]") { $user = $login[$i] -replace '[^a-zA-Z\.\]' write-host "Dropping $user" $logins[$i].Drop(); } } but that didn't work. Commented Jun 15, 2017 at 23:27
  • Just to clarify, your comment code is slightly different than your code snippet in that your comment code reads '$logins[$i].Drop()' rather than '$db.Users[$user].Drop()' is this just a typo or the code you ran in your script? Commented Jun 16, 2017 at 11:00
  • I've tried both - just trying to get it to work. Commented Jun 16, 2017 at 11:09
  • Switching it to $db.Users[$user].Drop() results in "Cannot index into a null array on if ($login[$i] -notmatch "[dbo]|[guest]|[INFORMATION_SCHEMA]|[sys]")" Commented Jun 16, 2017 at 11:29

1 Answer 1

2

I'm pretty certain the error message you are/were seeing is because you had set $logins to the list of $db.users and then tried to delete a user during the foreach loop.

Initializing $logins with $server.Logins (rather than $db.users) and then checking $db.Users.Contains($login) before calling $db.Users[$login].Drop() in your loop will fix your issue...

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.