11 April,19 at 11:50 AM
In the context of a demo for Forrester, I have been asked to explore the possibilty to automatically discover Windows systems and enrol them into the Cloud as resources managed by Centrify Privileges Service. The full request: "I would like to use Group Policy so when the Server startup it will register itself to the Cloud, can you help me?". After taking a moment to digest this, and being very familiar with both PowerShell and the RestAPI available with the Centrify Identity Platform, I answered "Yes of course! Let's do this".
I choose to wrap the RestAPI calls into a PowerShell script named cloud-self-register.ps1 that would be invoked using the startup script feature from a Computer Group Policy. This script is dropped onto the SYSVOL in my example, but as long as it's available on a read-for-everyone share you can put it wherever it suite you best.
Then I create a new Group Policy Object linked to an OU that contains Windows Servers in my Lab environment, this Group Policy use the PowerShell tab of the Scripts settings run at startup/login.
The GPO Settings, the full parameter line for this script being:
-Url oceanlab.my.centrify.com -Login resource-manager@oceanlab.my.centrify.com -Password -Account @{"User"="Administrator";"Password"=""}
And the result
Before the Group Policy applied, I only had one Windows server enroled manually into the Cloud on my CPS Tenant, as well as a couple of Linux servers (just so you know, CPS stands for Centrify Privilege Service).
But once the Group Policy applied, and the server is restarted, then the PowerShell script do its magic and let the server auto-enrol
We can see that I have now a new Server enrolled, without me doing nothing else that let the Server restart.
This is of course particularly helpful to let your servers enrol themselves without having to bother doing them one by one (afterall a good Admin is a lazy Admin). But also it helps not missing any of these new servers built on a high volume in these days of heavy virtualisation.
Ok... so you didn't read this article just to see me talking about GPO. You want to see the magic in the script. And maybe try it yourself as you may not believe it to be so simple.
Below an unpolished script doing the magic. These kind of functions will be soon turn into proper Cmdlets, but that's a very good example of what you can achieve already with the Centrify RestAPI and some help from the Professional Services team.
################################################ # Centrify Cloud Self Registering sample script # Created by Fabrice Viguier from sample work by Nick Gamb # # Author : Fabrice Viguier # Version: 1.1.302 First release # 1.2.305 Adding abiltity to register an Account right after self-registering ################################################ param ( [Parameter(Mandatory = $true, HelpMessage = "Specify the URL to use for the connction (by default cloud.centrify.com).")] [System.String]$Url, [Parameter(Mandatory = $false, HelpMessage = "Specify the Login name to use for the connection (User context will be used if not specified).")] [System.String]$Login, [Parameter(Mandatory = $false, HelpMessage = "Specify the Password to use for the connection (User will be prompted if not specified [RECOMMENDED]).")] [System.String]$Password, [Parameter(Mandatory = $false, HelpMessage = "Specify an Account to add to the Computer. Format has to be a Hash table: e.g. @{`"User`"=`"Administrator`", `"Password`"=`"Passw0rd`"}")] [System.Object]$Account ) # Debug preference if ($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent) { # Debug continue without waiting for confirmation $DebugPreference = "Continue" } else { # Debug message are turned off $DebugPreference = "SilentlyContinue" } try { # Setup variable for connection $Uri = ("https://{0}/Security/Login" -f $Url) $ContentType = "application/json" $Header = @{ "X-CENTRIFY-NATIVE-CLIENT" = "1" } Write-Host ("Connecting to Centrify Cloud Platform (https://{0})" -f $Url) # Validating Login if ([System.String]::IsNullOrEmpty($Login)) { # Get User from context $UserName = [System.Environment]::UserName $DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name $Login = ("{0}@{1}" -f $UserName, $DomainName) } # Validating Password if ([System.String]::IsNullOrEmpty($Password)) { # Prompt User for password Write-Host ("Login: {0}" -f $Login) $PasswordString = Read-Host ("Password" -f $Login) -AsSecureString $Password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($PasswordString)) } # Debug informations Write-Debug "--- CLOUD LOGIN ---" Write-Debug ("Uri= {0}" -f $Uri) Write-Debug ("ContentType= {0}" -f $ContentType) Write-Debug ("Login= {0}" -f $Login) Write-Debug ("Password= {0}" -f "******")# Password is masked in Debug # Format Json query $Json = ("{{`"user`":`"{0}`", `"password`":`"{1}`"`}}" -f $Login, $Password) # Connect using RestAPI $WebResponse = Invoke-WebRequest -Method Post -SessionVariable CipSession -Uri $Uri -Body $Json -ContentType $ContentType -Headers $Header -UseBasicParsing $WebResponseResult = $WebResponse.Content | ConvertFrom-Json if ($WebResponseResult.Success) { $PodFqdn = $WebResponseResult.Result.PodFqdn # Validate that a valid .ASPXAUTH cookie has been returned for the CipConnection $CookieUri = ("https://{0}" -f $PodFqdn) $ASPXAuth = $CipSession.Cookies.GetCookies($CookieUri) | Where-Object { $_.Name -eq ".ASPXAUTH" } if ([System.String]::IsNullOrEmpty($ASPXAuth)) { # .ASPXAuth cookie value is empty Throw ("Failed to get a .ASPXAuth cookie for Url {0}. Verify Url and try again." -f $CookieUri) } else { # Get FQDN for the current Computer $FQDN = [System.Net.Dns]::GetHostByName("").HostName # Registering Computer to the Cloud $Uri = ("https://{0}/ServerManage/AddResource" -f $PodFqdn) $ContentType = "application/json" $Header = @{ "X-CENTRIFY-NATIVE-CLIENT" = "1"; } # Debug informations Write-Debug "--- REGISTERING TO THE CLOUD ---" Write-Debug ("Uri= {0}" -f $Uri) Write-Debug ("ContentType= {0}" -f $ContentType) # Format Json query $Json = ("{{`"Name`":`"{0}`",`"FQDN`":`"{0}`",`"ComputerClass`":`"Windows`",`"SessionType`":`"Rdp`",`"Description`":`"Computer auto-enrolled using RestAPI.`"}}" -f $FQDN) # Connect using RestAPI $WebResponse = Invoke-WebRequest -Method Post -Uri $Uri -Body $Json -ContentType $ContentType -Headers $Header -WebSession $CipSession -UseBasicParsing $WebResponseResult = $WebResponse.Content | ConvertFrom-Json if ($WebResponseResult.Success) { Write-Host ("Computer {0} succefully Cloud registred." -f $FQDN) # Get the GUID of the registered Computer $ComputerID = $WebResponseResult.Result # Add Accounts if required if($Account -ne [Void]$null) { # Add an Account to the Computer $Uri = ("https://{0}/ServerManage/AddAccount" -f $PodFqdn) $ContentType = "application/json" $Header = @{ "X-CENTRIFY-NATIVE-CLIENT" = "1"; } # Debug informations Write-Debug "--- REGISTERING TO THE CLOUD ---" Write-Debug ("Uri= {0}" -f $Uri) Write-Debug ("ContentType= {0}" -f $ContentType) # Format Json query $Json = ("{{`"User`":`"{0}`",`"Password`":`"{1}`",`"IsManaged`":true,`"UseWheel`":false,`"Description`":`"`",`"Host`":`"{2}`"}}" -f $Account.User, $Account.Password, $ComputerID) # Connect using RestAPI $WebResponse = Invoke-WebRequest -Method Post -Uri $Uri -Body $Json -ContentType $ContentType -Headers $Header -WebSession $CipSession -UseBasicParsing $WebResponseResult = $WebResponse.Content | ConvertFrom-Json if ($WebResponseResult.Success) { Write-Host ("Managed Account {0} succefully added." -f $Account.User) } else { # Query error Throw $WebResponseResult.Message } } } else { if ($WebResponseResult.Message -match "Resource already exists") { Write-Warning ("Computer {0} is already Cloud registred. Skipping registration." -f $FQDN) } else { # Query error Throw $WebResponseResult.Message } } } } else { Throw $CisLogin.Message } } catch { Throw $_.Exception }