# Copyright: (c) 2024, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

DOCUMENTATION:
  module: service_account
  short_description: Manage Active Directory service account objects
  description:
    - Manages Active Directory service account objects and their attributes.
    - Currently this module only supports group managed service accounts
      (gMSA).
    - Before creating a gMSA, the AD environment must have created a KDS root
      key. See
      L(KDS Key,https://learn.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/create-the-key-distribution-services-kds-root-key)
      for more details. For the key to be effective immediately, set the
      effective time for 10 hours ago and do not use the
      C(-EffectiveImmediately) parameter. See the examples for more details.
  version_added: 1.7.0
  options:
    allowed_to_retrieve_password:
      description:
        - The principals that are allowed to retrieve the password for the
          service account to either add, remove, or set.
        - Each subkey value is a list of values in the form of a
          C(distinguishedName), C(objectGUID), C(objectSid), c(sAMAccountName),
          or C(userPrincipalName) string or a dictionary with the I(name) and
          optional I(server) key.
        - This value is built into a security descriptor by the ActiveDirectory
          cmdlet and set on the C(msDS-GroupMSAMembership) LDAP attribute.
        - This corresponds to the C(-PrincipalsAllowedToRetrieveManagedPassword)
          parameter on the AD cmdlets.
        - To clear all principals, use I(set) with an empty list.
        - See
          R(DN Lookup Attributes,ansible_collections.microsoft.ad.docsite.guide_attributes.dn_lookup_attributes)
          for more information on how DN lookups work.
        - See
          R(Setting list option values,ansible_collections.microsoft.ad.docsite.guide_list_values)
          for more information on how to add/remove/set list options.
      type: dict
      suboptions:
        add:
          description:
            - Adds the principals specified as principals allowed to retrieve
              the service account password.
            - Any existing principals not specified by I(add) will be untouched
              unless specified by I(remove) or not in I(set).
          type: list
          elements: raw
        lookup_failure_action:
          description:
            - Control the action to take when the lookup fails to find the DN.
            - C(fail) will cause the task to fail.
            - C(ignore) will ignore the value and continue.
            - C(warn) will ignore the value and display a warning.
          choices:
            - fail
            - ignore
            - warn
          default: fail
          type: str
        remove:
          description:
            - Removes the principals specified as principals allowed to
              retrieve the service account password.
            - Any existing pricipals not specified by I(remove) will be
              untouched unless I(set) is defined.
          type: list
          elements: raw
        set:
          description:
            - Sets the principals specified as principals allowed to retrieve
              the service account password.
            - This will remove any existing principals if not specified in this
              list.
            - Specify an empty list to remove all principals allowed to
              delegate.
          type: list
          elements: raw
    delegates:
      description:
        - The principal objects that the current AD object can trust for
          delegation to either add, remove or set.
        - This is also known as resource-based constrained delegation.
        - Each subkey value is a list of values in the form of a
          C(distinguishedName), C(objectGUID), C(objectSid), C(sAMAccountName),
          or C(userPrincipalName) string or a dictionary with the I(name) and
          optional I(server) key.
        - This is the value set on the
          C(msDS-AllowedToActOnBehalfOfOtherIdentity) LDAP attribute.
        - This is a highly sensitive attribute as it allows the principals
          specified to impersonate any account when authenticating with a
          service running as this managed account.
        - To clear all principals, use I(set) with an empty list.
        - See
          R(DN Lookup Attributes,ansible_collections.microsoft.ad.docsite.guide_attributes.dn_lookup_attributes)
          for more information on how DN lookups work.
        - See
          R(Setting list option values,ansible_collections.microsoft.ad.docsite.guide_list_values)
          for more information on how to add/remove/set list options.
      aliases:
        - principals_allowed_to_delegate
      type: dict
      suboptions:
        add:
          description:
            - Adds the principals specified as principals allowed to delegate
              to.
            - Any existing principals not specified by I(add) will be untouched
              unless specified by I(remove) or not in I(set).
          type: list
          elements: raw
        lookup_failure_action:
          description:
            - Control the action to take when the lookup fails to find the DN.
            - C(fail) will cause the task to fail.
            - C(ignore) will ignore the value and continue.
            - C(warn) will ignore the value and display a warning.
          choices:
            - fail
            - ignore
            - warn
          default: fail
          type: str
        remove:
          description:
            - Removes the principals specified as principals allowed to
              delegate to.
            - Any existing pricipals not specified by I(remove) will be
              untouched unless I(set) is defined.
          type: list
          elements: raw
        set:
          description:
            - Sets the principals specified as principals allowed to delegate
              to.
            - This will remove any existing principals if not specified in this
              list.
            - Specify an empty list to remove all principals allowed to
              delegate.
          type: list
          elements: raw
    dns_hostname:
      description:
        - Specifies the DNS name of the service account.
        - This is the value set on the C(dNSHostName) LDAP attribute.
        - This cannot be set when C(outbound_auth_only=True), otherwise it
          must be defined.
      type: str
    do_not_append_dollar_to_sam:
      description:
        - Do not automatically append C($) to the I(sam_account_name) value.
        - This only applies when I(sam_account_name) is explicitly set and can
          be used to create a service account without the C($) suffix.
      default: false
      type: bool
    enabled:
      description:
        - C(yes) will enable the service account.
        - C(no) will disable the service account.
      type: bool
    kerberos_encryption_types:
      description:
        - Specifies the Kerberos encryption types supported the AD service
          account.
        - This is the value set on the C(msDS-SupportedEncryptionTypes) LDAP
          attribute.
        - Avoid using C(rc4) or C(des) as they are older an insecure encryption
          protocols.
        - To clear all encryption types, use I(set) with an empty list.
        - See R(Setting list option values,ansible_collections.microsoft.ad.docsite.guide_list_values)
          for more information on how to add/remove/set list options.
      type: dict
      suboptions:
        add:
          description:
            - The encryption types to add to the existing set.
            - Any existing encryption types not specified by I(add) will be
              untouched unless specified by I(remove) or not in I(set).
          choices:
            - aes128
            - aes256
            - des
            - rc4
          type: list
          elements: str
        remove:
          description:
            - The encryption types to remove from the existing set.
            - Any existing encryption types not specified by I(remove) will be
              untouched unless I(set) is defined.
          choices:
            - aes128
            - aes256
            - des
            - rc4
          type: list
          elements: str
        set:
          description:
            - The encryption types to set as the only encryption types allowed
              by the AD service account.
            - This will remove any existing encryption types if not specified
              in this list.
            - Specify an empty list to remove all encryption types.
          choices:
            - aes128
            - aes256
            - des
            - rc4
          type: list
          elements: str
    outbound_auth_only:
      description:
        - Marks the service account for use with client outbound authentication
          only.
        - When set the service account can only be used for client roles only.
          For example it can only be used for outbound authentication attempts
          and cannot be used as a target authentication service principal.
        - If set then I(dns_hostname) cannot be set.
      default: false
      type: bool
    sam_account_name:
      description:
        - The C(sAMAccountName) value to set for the service account.
        - It has a maximum of 256 characters, 15 is advised for older operating
          systems compatibility.
        - If ommitted the value is the same as C(name$) when the service
          account is created.
        - Note that service account C(sAMAccountName) values typically end with
          a C($).
        - By default if the C($) suffix is omitted, it will be added to the
          end. If I(do_not_append_dollar_to_sam=True) then the provided value
          will be used as is without adding C($) to the end.
      type: str
    spn:
      description:
        - Specifies the service principal name(s) for the account to add,
          remove or set.
        - This is the value set on the C(servicePrincipalName) LDAP attribute.
        - To clear all service principal names, use I(set) with an empty list.
        - See
          R(Setting list option values,ansible_collections.microsoft.ad.docsite.guide_list_values)
          for more information on how to add/remove/set list options.
      aliases:
        - spns
      type: dict
      suboptions:
        add:
          description:
            - The SPNs to add to C(servicePrincipalName).
          type: list
          elements: str
        remove:
          description:
            - The SPNs to remove from C(servicePrincipalName).
          type: list
          elements: str
        set:
          description:
            - The SPNs to set as the only values in C(servicePrincipalName).
            - This will clear out any existing SPNs if not in the specified
              list.
            - Set to an empty list to clear all SPNs on the AD object.
          type: list
          elements: str
    trusted_for_delegation:
      description:
        - Specifies whether an account is trusted for Kerberos delegation.
        - This is also known as unconstrained Kerberos delegation.
        - This sets the C(ADS_UF_TRUSTED_FOR_DELEGATION) flag in the
          C(userAccountControl) LDAP attribute.
      type: bool
    upn:
      description:
        - Configures the User Principal Name (UPN) for the account.
        - The format is C(<username>@<domain>).
        - This is the value set on the C(userPrincipalName) LDAP attribute.
      type: str
  notes:
    - This module must be run on a Windows target host with the
      C(ActiveDirectory) module installed.
    - When matching by I(identity) with a C(sAMAccountName) value, the value
      should endd with C($). If the provided value does not end with C($) the
      module will still attempt to find the service account with the provided
      value before attempting a fallback lookup with C($) appended to the end.
  extends_documentation_fragment:
    - microsoft.ad.ad_object
    - ansible.builtin.action_common_attributes
  attributes:
    check_mode:
      support: full
    diff_mode:
      support: full
    platform:
      platforms:
        - windows
  seealso:
    - module: microsoft.ad.object_info
    - module: microsoft.ad.object
  author:
    - Jordan Borean (@jborean93)

EXAMPLES: |
  # A gMSA requires a KDS root key to be created. This key must be valid for
  # 10 hours before it can be used. This example creates the key and sets the
  # time for 10 hours ago to let it be used immediately. If your environment
  # uses multiple DCs you will still need to wait 10 hours for replication to
  # occur or target the DC you created the key on. Required Domain Admin or
  # Enterprise Admin privileges.
  - name: Create KDS root key if not present
    ansible.windows.win_powershell:
      error_action: stop
      script: |
        $Ansible.Changed = $false
        if (-not (Get-KdsRootKey)) {
            Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10))
            $Ansible.Changed = $true
        }

  - name: Create gMSA that allows Domain Admins to use
    microsoft.ad.service_account:
      identity: MyGMSA
      dns_hostname: MyGMSA.my_org.local
      description: GMSA for Domin Admins
      state: present
      allowed_to_retrieve_password:
        set:
          - Domain Admins

  - name: create gMSA that allows the ITFarmHosts computer account to retrieve the pass
    microsoft.ad.service_account:
      identity: ITFarm1
      dns_hostname: ITFarm1.contoso.com
      allowed_to_retrieve_password:
        set:
          - ITFarmHosts$
      kerberos_encryption_types:
        set:
          - aes128
          - aes256
      spn:
        add:
          - http/ITFarm1.contoso.com/contoso.com
          - http/ITFarm1.contoso.com/contoso
          - http/ITFarm1.contoso.com
          - http/ITFarm1.contoso

  - name: Remove gMSA by identity
    microsoft.ad.service_account:
      identity: ITFarm1$
      state: absent

  - name: Add SPNs to service account
    microsoft.ad.service_account:
      identity: MySA$
      spn:
        add:
          - HOST/MySA
          - HOST/MySA.domain.test
          - HOST/MySA.domain.test:1234

  - name: Remove SPNs on the service account
    microsoft.ad.service_account:
      identity: MySA$
      spn:
        remove:
          - HOST/MySA
          - HOST/MySA.domain.test
          - HOST/MySA.domain.test:1234

  - name: Add gMSA with sAMAccountName without $ suffix
    microsoft.ad.service_account:
      identity: MySA
      dns_hostname: MySA.contoso.com
      sam_account_name: MySA
      do_not_append_dollar_to_sam: true

RETURNS:
  object_guid:
    description:
      - The C(objectGUID) of the AD object that was created, removed, or
        edited.
      - If a new object was created in check mode, a GUID of 0s will be
        returned.
    returned: always
    type: str
    sample: d84a141f-2b99-4f08-9da0-ed2d26864ba1
  distinguished_name:
    description:
      - The C(distinguishedName) of the AD object that was created, removed,
        or edited.
    returned: always
    type: str
    sample: CN=act,CN=Managed Service Accounts,DC=domain,DC=test
  sid:
    description:
      - The Security Identifier (SID) of the account managed.
      - If a new serivce account was created in check mode, the SID will be
        C(S-1-5-0000).
    returned: always
    type: str
    sample: S-1-5-21-4151808797-3430561092-2843464588-1104
