locked
seed first user without using build in migration? RRS feed

  • Question

  • User100248066 posted

    In the shop I work, the built-in migration tools is great for development, they are not used to moving things to QA and production.   We use the 'dotnet ef migrations script' to generate scripts.  The only problem I face now is how to seed the first super user in the system.  Is there any way to do that via SQL or must I do that with an EXE of some sort?  Any good examples out there?


    Based on the first reply I realized I should provide info on what I am trying to script, the first reply addressed the creation of the roles, but not the user.  Right now when the app runs, it checks to see if the initial password is sent in the User Secrets, if it is, it then will run this code.  The question is, can I create this user in a script or is the Identity Framework doing some magic that cannot be scripted?  If so, what is the best way to get this first user created?

            private static async Task CreateSuperUser(IServiceProvider serviceProvider, string initialUserPw, string email, string username
                , string fullname, string phone)
            {
                var id = await EnsureUser(username, initialUserPw, email, fullname, phone, serviceProvider);
    
                Task.WaitAll(new Task[]
                {
                    EnsureRole(id, Constants.SuperUserRole, serviceProvider)
                    , EnsureRole(id, Constants.AdminRole, serviceProvider)
                    , EnsureRole(id, Constants.UsersRole, serviceProvider)
                });
            }
    
    
            private static async Task<string> EnsureUser(string username,
                string testUserPw, string email, string name
                , string phone, IServiceProvider serviceProvider)
            {
                var userManager = serviceProvider.GetService<UserManager<FacilityManagerUser>>();
    
                var user = await userManager.FindByNameAsync(username);
                if (user == null)
                {
                    user = new FacilityManagerUser
                    {
                        UserName = username
                        , Email = email
                        , FullName = name
                        , EmailConfirmed = true
                        , PhoneNumber = phone
                        , PhoneNumberConfirmed = true
                    };
                    await userManager.CreateAsync(user, testUserPw);
                }
    
                if (user == null)
                {
                    throw new Exception("The password is probably not strong enough!");
                }
    
                return user.Id;
            }
    
            private static async Task<IdentityResult> EnsureRole(string uid, string role, IServiceProvider serviceProvider)
            {
                IdentityResult identityResult = null;
    
                var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
    
                if (roleManager == null)
                {
                    throw new Exception("roleManager null");
                }
    
                if (!await roleManager.RoleExistsAsync(role))
                {
                    identityResult = await roleManager.CreateAsync(new IdentityRole(role));
                }
    
                var userManager = serviceProvider.GetService<UserManager<FacilityManagerUser>>();
    
                var user = await userManager.FindByIdAsync(uid);
    
                if (user == null)
                {
                    throw new Exception("No user found, the initial password was probably not strong enough!");
                }
    
                identityResult = await userManager.AddToRoleAsync(user, role);
    
                return identityResult;
            }
    

    Tuesday, April 6, 2021 3:09 PM

All replies

  • User475983607 posted

    scarleton

    In the shop I work, the built-in migration tools is great for development, they are not used to moving things to QA and production.   We use the 'dotnet ef migrations script' to generate scripts.  The only problem I face now is how to seed the first super user in the system.  Is there any way to do that via SQL or must I do that with a EXE of some sort?  Any good examples out there?

    I add an insert script to the migrations to seed data.  Just create a new migration and add your insert script or modify an existing migration that has not been deployed.

    {
        public partial class SeedClaimsMigration : Migration
        {
            protected override void Up(MigrationBuilder migrationBuilder)
            {
                migrationBuilder.Sql(@"
    BEGIN TRANSACTION;  
    
    BEGIN TRY  
    	--
    	--INSERT UserClaims 
    	-- 
    	INSERT INTO [dbo].[AspNetUserClaims] (
    		 [ClaimType]
    		,[ClaimValue]
    		,[UserId]
    	)
    	SELECT 'http://schemas.mydomain.com/2018/04/identity/claims/myclaim',
    			u.access_code, 
    			au.id
    	FROM db.dbo.user AS u
    		INNER JOIN [dbo].[AspNetUsers] AS au ON u.user_id = au.Id 
    
    END TRY  
    BEGIN CATCH  
        SELECT   
            ERROR_NUMBER() AS ErrorNumber  
            ,ERROR_SEVERITY() AS ErrorSeverity  
            ,ERROR_STATE() AS ErrorState  
            ,ERROR_PROCEDURE() AS ErrorProcedure  
            ,ERROR_LINE() AS ErrorLine  
            ,ERROR_MESSAGE() AS ErrorMessage;  
    
        IF @@TRANCOUNT > 0  
            ROLLBACK TRANSACTION;
    		
    	PRINT 'Transaction failed'  
    END CATCH;  
    
    IF @@TRANCOUNT > 0  
        COMMIT TRANSACTION;  
    	PRINT 'Transaction completed successfully'
    GO ");
            }
    
            protected override void Down(MigrationBuilder migrationBuilder)
            {
                migrationBuilder.Sql(@"DELETE FROM [AspNetUserClaims] WHERE [ClaimType] = 'http://schemas.mydomain.com/2018/04/identity/claims/myclaim'");
            }
        }

    Otherwise, just add the insert script to the script generated by the dotnet ef migrations script.

    Edit: and the docs cover how to seed data.

    https://docs.microsoft.com/en-us/ef/core/modeling/data-seeding

    Tuesday, April 6, 2021 3:18 PM
  • User100248066 posted

    Thank you for that code, that does answer part of my question,  but not about creating the actual user.  I added code to my original post so you can see everything that needs to be scripted. 

    Tuesday, April 6, 2021 4:17 PM
  • User475983607 posted

    You can create an Initialize method that gets the services needed to create an Identity account when the application starts.

    private void InitializesuperUser(IApplicationBuilder app)
    {
        using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
        {
            var serviceProvider = serviceScope.ServiceProvider.GetRequiredService<IServiceProvider>();
            
            ...
        }
    
    }

    Call the method from Configure().

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        InitializesuperUser(app);

    You'll want to make sure the user does not exist before creating the user.  You also need to make sure the Identity tables have been created.

    Tuesday, April 6, 2021 6:22 PM