summaryrefslogtreecommitdiffstats
path: root/ansible_collections/microsoft/ad/plugins/modules/membership.ps1
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/microsoft/ad/plugins/modules/membership.ps1')
-rw-r--r--ansible_collections/microsoft/ad/plugins/modules/membership.ps1270
1 files changed, 270 insertions, 0 deletions
diff --git a/ansible_collections/microsoft/ad/plugins/modules/membership.ps1 b/ansible_collections/microsoft/ad/plugins/modules/membership.ps1
new file mode 100644
index 000000000..2b37bcdfd
--- /dev/null
+++ b/ansible_collections/microsoft/ad/plugins/modules/membership.ps1
@@ -0,0 +1,270 @@
+#!powershell
+
+# Copyright (c) 2022 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#AnsibleRequires -CSharpUtil Ansible.Basic
+
+$spec = @{
+ options = @{
+ dns_domain_name = @{
+ type = 'str'
+ }
+ domain_admin_user = @{
+ type = 'str'
+ }
+ domain_admin_password = @{
+ no_log = $true
+ type = 'str'
+ }
+ domain_ou_path = @{
+ type = 'str'
+ }
+ hostname = @{
+ type = 'str'
+ }
+ offline_join_blob = @{
+ type = "str"
+ no_log = $true
+ }
+ reboot = @{
+ default = $false
+ type = 'bool'
+ }
+ state = @{
+ choices = 'domain', 'workgroup'
+ required = $true
+ type = 'str'
+ }
+ workgroup_name = @{
+ type = 'str'
+ }
+ }
+ mutually_exclusive = @(
+ @('offline_join_blob', 'domain_admin_user'),
+ @('offline_join_blob', 'dns_domain_name'),
+ @('offline_join_blob', 'domain_ou_path'),
+ @('offline_join_blob', 'hostname')
+ )
+ required_if = @(
+ @('state', 'domain', @('domain_admin_user', 'offline_join_blob'), $true),
+ @('state', 'workgroup', @('workgroup_name', 'domain_admin_user', 'domain_admin_password'))
+ )
+ required_together = @(
+ , @('domain_admin_user', 'domain_admin_password')
+ )
+ supports_check_mode = $true
+}
+$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
+
+$module.Result.reboot_required = $false
+$module.Diff.before = @{}
+$module.Diff.after = @{}
+
+$dnsDomainName = $module.Params.dns_domain_name
+$domainCredential = if ($module.Params.domain_admin_user) {
+ New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList @(
+ $module.Params.domain_admin_user,
+ (ConvertTo-SecureString -AsPlainText -Force -String $module.Params.domain_admin_password)
+ )
+}
+$domainOUPath = $module.Params.domain_ou_path
+$hostname = $module.Params.hostname
+$state = $module.Params.state
+$workgroupName = $module.Params.workgroup_name
+
+Add-CSharpType -AnsibleModule $module -References @'
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+namespace microsoft.ad.membership
+{
+ [Flags]
+ public enum ProvisionOptions
+ {
+ None = 0,
+ NETSETUP_PROVISION_ONLINE_CALLER = 0x40000000,
+ }
+
+ public static class Native
+ {
+ [DllImport("Netapi32.dll", EntryPoint = "NetRequestOfflineDomainJoin")]
+ private static extern int NativeNetRequestOfflineDomainJoin(
+ IntPtr pProvisionBinData,
+ int cbProvisionBinDataSize,
+ int dwOptions,
+ [MarshalAs(UnmanagedType.LPWStr)] string lpWindowsPath);
+
+ public static void NetRequestOfflineDomainJoin(byte[] data, ProvisionOptions options,
+ string windowsPath)
+ {
+ IntPtr buffer = Marshal.AllocHGlobal(data.Length);
+ try
+ {
+ Marshal.Copy(data, 0, buffer, data.Length);
+
+ int res = NativeNetRequestOfflineDomainJoin(buffer, data.Length, (int)options, windowsPath);
+ if (res != 0)
+ {
+ throw new Win32Exception(res);
+ }
+ }
+ finally {
+ Marshal.FreeHGlobal(buffer);
+ }
+ }
+ }
+}
+'@
+
+Function Get-CurrentState {
+ <#
+ .SYNOPSIS
+ Gets the current state of the host.
+ #>
+ [CmdletBinding()]
+ param ()
+
+ $cs = Get-CimInstance -ClassName Win32_ComputerSystem -Property Domain, PartOfDomain, Workgroup
+ $domainName = if ($cs.PartOfDomain) {
+ try {
+ [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain().Name
+ }
+ catch [System.Security.Authentication.AuthenticationException] {
+ # This might happen if running as a local user on a host already
+ # joined to the domain. Just try the Win32_ComputerSystem fallback
+ # value.
+ $cs.Domain
+ }
+ }
+ else {
+ $null
+ }
+
+ [PSCustomObject]@{
+ HostName = $env:COMPUTERNAME
+ PartOfDomain = $cs.PartOfDomain
+ DnsDomainName = $domainName
+ WorkgroupName = $cs.Workgroup
+ }
+}
+
+$currentState = Get-CurrentState
+
+$module.Diff.before = @{
+ dns_domain_name = $currentState.DnsDomainName
+ hostname = $currentState.HostName
+ state = if ($currentState.PartOfDomain) { 'domain' } else { 'workgroup' }
+ workgroup_name = $currentState.WorkgroupName
+}
+if (-not $hostname) {
+ $hostname = $currentState.HostName
+}
+
+if ($state -eq 'domain') {
+ if ($module.Params.offline_join_blob) {
+ # FUTURE: Read blob to see what domain it is for.
+ if (-not $currentState.PartOfDomain) {
+ try {
+ if (-not $module.CheckMode) {
+ [microsoft.ad.membership.Native]::NetRequestOfflineDomainJoin(
+ [System.Convert]::FromBase64String($module.Params.offline_join_blob),
+ "NETSETUP_PROVISION_ONLINE_CALLER",
+ $env:SystemRoot)
+ }
+ }
+ catch [System.ComponentModel.Win32Exception] {
+ $msg = "Failed to perform offline domain join (0x{0:X8}): {1}" -f $_.Exception.NativeErrorCode, $_.Exception.Message
+ $module.FailJson($msg, $_)
+ }
+
+ $module.Result.changed = $true
+ $module.Result.reboot_required = $true
+ }
+ }
+ else {
+ if ($dnsDomainName -ne $currentState.DnsDomainName) {
+ if ($currentState.PartOfDomain) {
+ $module.FailJson("Host is already joined to '$($currentState.DnsDomainName)', switching domains is not implemented")
+ }
+
+ $joinParams = @{
+ ComputerName = '.'
+ Credential = $domainCredential
+ DomainName = $dnsDomainName
+ Force = $true
+ WhatIf = $module.CheckMode
+ }
+ if ($hostname -ne $currentState.HostName) {
+ $joinParams.NewName = $hostname
+
+ # By setting this here, the Rename-Computer call is skipped as
+ # joining the domain will rename the host for us.
+ $hostname = $currentState.HostName
+ }
+ if ($domainOUPath) {
+ $joinParams.OUPath = $domainOUPath
+ }
+
+ Add-Computer @joinParams
+
+ $module.Result.changed = $true
+ $module.Result.reboot_required = $true
+ }
+ }
+}
+else {
+ if ($workgroupName -ne $currentState.WorkgroupName) {
+ if ($currentState.PartOfDomain) {
+ $removeParams = @{
+ UnjoinDomainCredential = $domainCredential
+ Workgroup = $workgroupName
+ Force = $true
+ WhatIf = $module.CheckMode
+ }
+
+ Remove-Computer @removeParams
+ }
+ elseif (-not $module.CheckMode) {
+ try {
+ $res = Get-CimInstance Win32_ComputerSystem | Invoke-CimMethod -MethodName JoinDomainOrWorkgroup -Arguments @{
+ Name = $workgroupName
+ }
+ }
+ catch {
+ $module.FailJson("Failed to set workgroup as '$workgroupName': $($_.Exception.Message)", $_)
+ }
+
+ if ($res.ReturnValue -ne 0) {
+ $msg = [System.ComponentModel.Win32Exception]$res.ReturnValue
+ $module.FailJson("Failed to set workgroup as '$workgroupName', return value: $($res.ReturnValue): $msg")
+ }
+ }
+
+ $module.Result.changed = $true
+ $module.Result.reboot_required = $true
+ }
+}
+
+if ($hostname -ne $currentState.Hostname) {
+ $renameParams = @{
+ DomainCredential = $domainCredential
+ NewName = $hostname
+ WhatIf = $module.CheckMode
+ Force = $true
+ }
+ Rename-Computer @renameParams
+
+ $module.Result.changed = $true
+ $module.Result.reboot_required = $true
+}
+
+$module.Diff.after = @{
+ dns_domain_name = $dnsDomainName
+ hostname = $hostname
+ state = $state
+ workgroup_name = $workgroupName
+}
+
+$module.ExitJson()