mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-09-07 21:54:59 +08:00
Remove ACTIVEDIRECTORY_PS (#1680)
This commit is contained in:
parent
f24c250c46
commit
83a4b6a2f4
19 changed files with 7 additions and 837 deletions
1
OWNERS
1
OWNERS
|
@ -1,4 +1,3 @@
|
|||
# providers/activedir DEPRECATED
|
||||
providers/akamaiedgedns @svernick
|
||||
providers/autodns @arnoschoon
|
||||
providers/axfrddns @hnrgrgr
|
||||
|
|
|
@ -17,7 +17,6 @@ Windows). The provider model is extensible, so more providers can be added.
|
|||
Currently supported DNS providers:
|
||||
- AWS Route 53
|
||||
- AXFR+DDNS
|
||||
- Active Directory (Deprecated, see Microsoft DNS)
|
||||
- Akamai Edge DNS
|
||||
- AutoDNS
|
||||
- Azure DNS
|
||||
|
@ -76,7 +75,7 @@ You can think of it as a DNS compiler. The configuration files are
|
|||
written in a DSL that looks a lot like JavaScript. It is compiled
|
||||
to an intermediate representation (IR). Compiler back-ends use the
|
||||
IR to update your DNS zones on services such as Route53, Cloudflare,
|
||||
and Gandi, or systems such as BIND and Active Directory.
|
||||
and Gandi, or systems such as BIND.
|
||||
|
||||
# An Example
|
||||
|
||||
|
@ -189,7 +188,6 @@ See [dnscontrol-action](https://github.com/koenrh/dnscontrol-action)
|
|||
* **ACME/Let's Encrypt support is frozen and will be removed after December 31, 2022.** The `get-certs` command (renews certs via Let's Encrypt) has no maintainer. There are other projects that do a better job. If you don't use this feature, please do not start. If you do use this feature, please plan on migrating to something else. See discussion in https://github.com/StackExchange/dnscontrol/issues/1400
|
||||
* **Provider OCTODNS is frozen and will be removed after Nov 1, 2022.** It was written as a joke and nobody laughed. It's time to remove the code and move on.
|
||||
* **convertzone is frozen and will be removed after June 30, 2022.** The `convertzone` stand-alone program is replaced by the `get-zone` subcommand of DNSControl. It does everything `convertzone` did and more.
|
||||
* **Provider ACTIVEDIRECTORY_PS is frozen and will be removed after June 30, 2022.** It is replaced by MSDNS which is 100% feature compatible and works better. (We believe nobody uses it.)
|
||||
* **get-zones syntax changes in v3.16** Starting in v3.16, the command line arguments for `dnscontrol get-zones` changes. For backwards compatibility change `provider` to `-`. See documentation for details.
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="rotate"><div><span>ACTIVEDIRECTORY_PS</span></div></th>
|
||||
<th class="rotate"><div><span>AKAMAIEDGEDNS</span></div></th>
|
||||
<th class="rotate"><div><span>AUTODNS</span></div></th>
|
||||
<th class="rotate"><div><span>AXFRDDNS</span></div></th>
|
||||
|
@ -52,9 +51,6 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="This means the provider is actively used at Stack Exchange, bugs are more likely to be fixed, and failing integration tests will block a release. See below for details">Official Support</th>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -217,9 +213,6 @@
|
|||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -331,9 +324,6 @@
|
|||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -442,9 +432,6 @@
|
|||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -531,8 +518,8 @@
|
|||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
|
@ -540,7 +527,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can automatically manage DNSSEC">AUTODNSSEC</th>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -611,9 +597,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can manage CAA records">CAA</th>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -724,9 +707,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports adding PTR records for reverse lookup zones">PTR</th>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -835,7 +815,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can manage NAPTR records">NAPTR</th>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -912,7 +891,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can manage SOA records">SOA</th>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -963,9 +941,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Driver has explicitly implemented SRV record management">SRV</th>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1082,7 +1057,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can manage SSHFP records">SSHFP</th>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1173,7 +1147,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can manage TLSA records">TLSA</th>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1336,7 +1309,6 @@
|
|||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="Using ALIAS is possible through our extended DNS (X-DNS) service. Feel free to get in touch with us.">
|
||||
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1370,7 +1342,6 @@
|
|||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1420,7 +1391,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports adding DS records">DS</th>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1501,7 +1471,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports adding AKAMAICDN records">AKAMAICDN</th>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1544,12 +1513,10 @@
|
|||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
<td><i class="fa fa-minus dim"></i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="This provider is recommended for use in 'dual hosting' scenarios. Usually this means the provider allows full control over the apex NS records">dual host</th>
|
||||
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="This driver does not manage NS records, so should not be used for dual-host scenarios">
|
||||
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1656,9 +1623,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="This means the provider can automatically create domains that do not currently exist on your account. The 'dnscontrol create-domains' command will initialize any missing domains">create-domains</th>
|
||||
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="AD depends on the zone already existing on the dns server">
|
||||
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1785,9 +1749,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="indicates you can use NO_PURGE macro to prevent deleting records not managed by dnscontrol. A few providers that generate the entire zone from scratch have a problem implementing this.">no_purge</th>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="danger">
|
||||
<i class="fa fa-times text-danger" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
@ -1914,9 +1875,6 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="indicates the dnscontrol get-zones subcommand is implemented.">get-zones</th>
|
||||
<td class="info">
|
||||
<i class="fa fa-circle-o text-info" aria-hidden="true"></i>
|
||||
</td>
|
||||
<td class="success">
|
||||
<i class="fa fa-check text-success" aria-hidden="true"></i>
|
||||
</td>
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
---
|
||||
name: ActiveDirectory_PS
|
||||
layout: default
|
||||
jsId: ACTIVEDIRECTORY_PS
|
||||
title: ActiveDirectory_PS Provider
|
||||
---
|
||||
|
||||
# WARNING:
|
||||
|
||||
WARNING: This provider is deprecated and will eventually be removed.
|
||||
Please switch to MSDNS. It is more modern and reliable. The
|
||||
`creds.json` fields changed names; otherwise it should be an
|
||||
uneventful upgrade.
|
||||
|
||||
# ActiveDirectory_PS Provider
|
||||
This provider updates an Microsoft Active Directory server DNS server. It interacts with AD via PowerShell commands that are generated and executed on the local machine. This means that DNSControl must be run on a Windows host. This driver automatically deactivates itself when run on non-Windows systems.
|
||||
|
||||
# Running on Non-Windows systems
|
||||
For debugging and testing on non-Windows systems, a "fake PowerShell" mode can be used, which will activate the driver and simulate PowerShell as follows:
|
||||
|
||||
- **Zone Input**: Normally when DNSControl needs to know the contents of an existing DNS zone, it generates a PowerShell command to gather such information and saves a copy in a file called `adzonedump.ZONE.json` (where "ZONE" is replaced with the zone name). When "fake PowerShell" mode is enabled, the PowerShell command is not run, but the `adzonedump.ZONE.json` file is read. You must generate this file ahead of time (often on a different machine, one that runs PowerShell).
|
||||
- **Zone Changes**: Normally when DNSControl needs to change DNS records, it executes PowerShell commands as required. When "fake PowerShell" mode is enabled, these commands are simply logged to a file `dns_update_commands.ps1` and the system assumes they executed.
|
||||
|
||||
To activate this mode, set `"fakeps":"true"` inside your credentials file for the provider.
|
||||
|
||||
## Configuration
|
||||
|
||||
The `ActiveDirectory_PS` provider reads an `ADServer` setting from `creds.json` to know the name of the ActiveDirectory DNS Server to update.
|
||||
|
||||
```js
|
||||
{
|
||||
"activedir": {
|
||||
"TYPE": "ACTIVEDIRECTORY_PS",
|
||||
"ADServer": "ny-dc01"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
If you want to modify the "fake powershell" mode details, you can set them in the credentials file:
|
||||
|
||||
```js
|
||||
{
|
||||
"activedir": {
|
||||
"TYPE": "ACTIVEDIRECTORY_PS",
|
||||
"ADServer": "ny-dc01",
|
||||
"fakeps": "true",
|
||||
"pslog": "powershell.log",
|
||||
"psout": "commandsToRun.ps1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
An example DNS configuration:
|
||||
|
||||
```js
|
||||
var REG_NONE = NewRegistrar("none", "NONE")
|
||||
var DSP_ACTIVEDIRECTORY = NewDnsProvider("activedir", "ACTIVEDIRECTORY_PS");
|
||||
|
||||
D("example.tld", REG_NONE, DnsProvider(DSP_ACTIVEDIRECTORY),
|
||||
A("test", "1.2.3.4")
|
||||
)
|
||||
```
|
||||
|
||||
To generate a `adzonedump.ZONE.json` file, run `dnscontrol preview` on a Windows system then copy the appropriate file to the system you'll use in "fake powershell" mode.
|
||||
|
||||
The `adzonedump.ZONE.json` files should be UTF-16LE encoded. If you hand-craft such a file on a non-Windows system, you may need to convert it from UTF-8 to UTF-16LE using:
|
||||
|
||||
iconv -f UTF8 -t UTF-16LE <adzonedump.FOO.json.utf0 > adzonedump.FOO.json
|
||||
|
||||
If you check these files into Git, you should mark them as "binary" in `.gitattributes`.
|
|
@ -16,8 +16,6 @@ non-Windows systems.
|
|||
DNSControl will use `New-PSSession` to execute the commands remotely if
|
||||
`computername` is set in `creds.json` (see below).
|
||||
|
||||
This provider will replace `ACTIVEDIRECTORY_PS` which is deprecated.
|
||||
|
||||
# Caveats
|
||||
|
||||
* Two systems updating a zone is never a good idea. If Windows Dynamic
|
||||
|
@ -65,37 +63,3 @@ D("example.tld", REG_NONE, DnsProvider(DSP_MSDNS),
|
|||
A("test", "1.2.3.4")
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
# Converting from `ACTIVEDIRECTORY_PS`
|
||||
|
||||
If you were using the `ACTIVEDIRECTORY_PS` provider and are switching to `MSDNS`, make the following changes:
|
||||
|
||||
1. In `dnsconfig.js`, change `ACTIVEDIRECTORY_PS` to `MSDNS` in any `NewDnsProvider()` calls.
|
||||
|
||||
2. In `creds.json`: Since unused fields are quietly ignored, it is
|
||||
safe to list both the old and new options:
|
||||
a. Add a field "dnsserver" with the DNS server's name. (OPTIONAL if dnscontrol is run on the DNS server.)
|
||||
b. If the PowerShell commands need to be run on a different host using a `PSSession`, add `pssession: "remoteserver",` where `remoteserver` is the name of the server where the PowerShell commands should run.
|
||||
c. The MSDNS provider will quietly ignore `fakeps`, `pslog` and `psout`. Feel free to leave them in `creds.json` until you are sure you aren't going back to the old provider.
|
||||
|
||||
During the transition your `creds.json` file might look like:
|
||||
|
||||
```json
|
||||
{
|
||||
"msdns": {
|
||||
"ADServer": "ny-dc01", << Delete these after you have
|
||||
"fakeps": "true", << verified that MSDNS works
|
||||
"pslog": "log.txt", << properly.
|
||||
"psout": "out.txt",
|
||||
"dnsserver": "ny-dc01",
|
||||
"pssession": "mywindowshost"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Run `dnscontrol preview` to make sure the provider works as expected.
|
||||
|
||||
4. If for any reason you need to revert, simply change `dnsconfig.js` to refer to `ACTIVEDIRECTORY_PS` again (or use `git` commands). If you are reverting because you found a bug, please [file an issue](https://github.com/StackExchange/dnscontrol/issues/new).
|
||||
|
||||
5. Once you are confident in the new provider, remove `ADServer`, `fakeps`, `pslog`, `psout` from `creds.json`.
|
||||
|
|
|
@ -47,7 +47,6 @@ Official support means:
|
|||
|
||||
Providers in this category and their maintainers are:
|
||||
|
||||
* `ACTIVEDIRECTORY_PS` @tlimoncelli
|
||||
* `AZURE_DNS` @vatsalyagoel
|
||||
* `BIND` @tlimoncelli
|
||||
* `GCLOUD` @riyadhalnur
|
||||
|
|
|
@ -92,7 +92,7 @@ Pick a similar provider as your base. Providers basically fall
|
|||
into three general categories:
|
||||
|
||||
* **zone:** The API requires you to upload the entire zone every time. (BIND, NAMECHEAP).
|
||||
* **incremental-record:** The API lets you add/change/delete individual DNS records. (ACTIVEDIR, CLOUDFLARE, DNSIMPLE, NAMEDOTCOM, GCLOUD, HEXONET)
|
||||
* **incremental-record:** The API lets you add/change/delete individual DNS records. (CLOUDFLARE, DNSIMPLE, NAMEDOTCOM, GCLOUD, HEXONET)
|
||||
* **incremental-label:** Like incremental-record, but if there are
|
||||
multiple records on a label (for example, example www.example.com
|
||||
has A and MX records), you have to replace all the records at that
|
||||
|
|
|
@ -749,7 +749,6 @@ func makeTests(t *testing.T) []*TestGroup {
|
|||
),
|
||||
|
||||
testgroup("MX",
|
||||
not("ACTIVEDIRECTORY_PS"), // Not implemented.
|
||||
tc("MX record", mx("@", 5, "foo.com.")),
|
||||
tc("Second MX record, same prio", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com.")),
|
||||
tc("3 MX", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com."), mx("@", 15, "foo3.com.")),
|
||||
|
@ -1077,7 +1076,7 @@ func makeTests(t *testing.T) []*TestGroup {
|
|||
),
|
||||
|
||||
// ClouDNS provider can work with PTR records, but you need to create special type of zone
|
||||
testgroup("PTR", requires(providers.CanUsePTR), not("ACTIVEDIRECTORY_PS", "CLOUDNS"),
|
||||
testgroup("PTR", requires(providers.CanUsePTR), not("CLOUDNS"),
|
||||
tc("Create PTR record", ptr("4", "foo.com.")),
|
||||
tc("Modify PTR record", ptr("4", "bar.com.")),
|
||||
),
|
||||
|
@ -1094,7 +1093,7 @@ func makeTests(t *testing.T) []*TestGroup {
|
|||
tc("Modify SOA minttl", soa("@", "mmm.ns.cloudflare.com.", "eee.cloudflare.com.", 2037190000, 10001, 2401, 604801, 3601)),
|
||||
),
|
||||
|
||||
testgroup("SRV", requires(providers.CanUseSRV), not("ACTIVEDIRECTORY_PS"),
|
||||
testgroup("SRV", requires(providers.CanUseSRV),
|
||||
tc("SRV record", srv("_sip._tcp", 5, 6, 7, "foo.com.")),
|
||||
tc("Second SRV record, same prio", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 5, 60, 70, "foo2.com.")),
|
||||
tc("3 SRV", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 5, 60, 70, "foo2.com."), srv("_sip._tcp", 15, 65, 75, "foo3.com.")),
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
{
|
||||
"ACTIVEDIRECTORY_PS": {
|
||||
"ADServer": "$AD_SERVER",
|
||||
"domain": "$AD_DOMAIN"
|
||||
},
|
||||
"AUTODNS": {
|
||||
"username": "$AUTODNS_USERNAME",
|
||||
"password": "$AUTODNS_PASSWORD",
|
||||
|
|
|
@ -3,7 +3,6 @@ package all
|
|||
|
||||
import (
|
||||
// Define all known providers here. They should each register themselves with the providers package via init function.
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/activedir"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/akamaiedgedns"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/autodns"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/axfrddns"
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
package activedir
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
|
||||
"runtime"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/providers"
|
||||
)
|
||||
|
||||
// This is the struct that matches either (or both) of the Registrar and/or DNSProvider interfaces:
|
||||
type activedirProvider struct {
|
||||
adServer string
|
||||
fake bool
|
||||
psOut string
|
||||
psLog string
|
||||
}
|
||||
|
||||
var features = providers.DocumentationNotes{
|
||||
providers.CanGetZones: providers.Unimplemented(),
|
||||
providers.CanUseAlias: providers.Cannot(),
|
||||
providers.CanUseCAA: providers.Cannot(),
|
||||
providers.CanUsePTR: providers.Cannot(),
|
||||
providers.CanUseSRV: providers.Cannot(),
|
||||
providers.DocCreateDomains: providers.Cannot("AD depends on the zone already existing on the dns server"),
|
||||
providers.DocDualHost: providers.Cannot("This driver does not manage NS records, so should not be used for dual-host scenarios"),
|
||||
providers.DocOfficiallySupported: providers.Can(),
|
||||
}
|
||||
|
||||
// Register with the dnscontrol system.
|
||||
// This establishes the name (all caps), and the function to call to initialize it.
|
||||
func init() {
|
||||
fns := providers.DspFuncs{
|
||||
Initializer: newDNS,
|
||||
RecordAuditor: AuditRecords,
|
||||
}
|
||||
providers.RegisterDomainServiceProviderType("ACTIVEDIRECTORY_PS", fns, features)
|
||||
}
|
||||
|
||||
func newDNS(config map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
|
||||
printer.Printf("WARNING: ACTIVEDIRECTORY_PS provider is being replaced by MSDNS. Please convert. Details in https://stackexchange.github.io/dnscontrol/providers/msdns\n")
|
||||
|
||||
fake := false
|
||||
if fVal := config["fakeps"]; fVal == "true" {
|
||||
fake = true
|
||||
} else if fVal != "" && fVal != "false" {
|
||||
return nil, fmt.Errorf("fakeps value must be 'true' or 'false'")
|
||||
}
|
||||
|
||||
psOut, psLog := config["psout"], config["pslog"]
|
||||
if psOut == "" {
|
||||
psOut = "dns_update_commands.ps1"
|
||||
}
|
||||
if psLog == "" {
|
||||
psLog = "powershell.log"
|
||||
}
|
||||
|
||||
p := &activedirProvider{psLog: psLog, psOut: psOut, fake: fake}
|
||||
if fake {
|
||||
return p, nil
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
srv := config["ADServer"]
|
||||
if srv == "" {
|
||||
return nil, fmt.Errorf("ADServer required for Active Directory provider")
|
||||
}
|
||||
p.adServer = srv
|
||||
return p, nil
|
||||
}
|
||||
printer.Printf("WARNING: PowerShell not available. Active Directory will not be updated.\n")
|
||||
return providers.None{}, nil
|
||||
}
|
Binary file not shown.
|
@ -1,11 +0,0 @@
|
|||
package activedir
|
||||
|
||||
import (
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
)
|
||||
|
||||
// AuditRecords returns an error if any records are not
|
||||
// supportable by this provider.
|
||||
func AuditRecords(records []*models.RecordConfig) error {
|
||||
return nil
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
### Active Directory
|
||||
|
||||
This provider updates a DNS Zone in an Active Directory Integrated Zone.
|
||||
|
||||
When run on Windows, AD is updated directly. The code generates
|
||||
PowerShell commands, executes them, and checks the results.
|
||||
It leaves behind a log file of the commands that were generated.
|
||||
|
||||
When run on non-Windows, AD isn't updated because we can't execute
|
||||
PowerShell at this time. Instead of reading the existing zone data
|
||||
from AD, It learns what
|
||||
records are in the zone by reading
|
||||
`adzonedump.{ZONENAME}.json`, a file that must be created beforehand.
|
||||
It does not actually update AD, it generates a file with PowerShell
|
||||
commands that would do the updates, which you must execute afterwords.
|
||||
If the `adzonedump.{ZONENAME}.json` does not exist, the zone is quietly skipped.
|
||||
|
||||
Not implemented:
|
||||
|
||||
* Delete records. This provider will not delete any records. It will only add
|
||||
and change existing records. See "Note to future devs" below.
|
||||
* Update TTLs. It ignores TTLs.
|
||||
|
||||
|
||||
## required creds.json config
|
||||
|
||||
No "creds.json" configuration is expected.
|
||||
|
||||
## example dns config js:
|
||||
|
||||
```js
|
||||
var REG_NONE = NewRegistrar('none', 'NONE')
|
||||
var DSP_ACTIVEDIRECTORY_DS = NewDSP("activedir", "ACTIVEDIRECTORY_PS");
|
||||
|
||||
D('ds.stackexchange.com', REG_NONE,
|
||||
DSP_ACTIVEDIRECTORY_DS,
|
||||
)
|
||||
|
||||
|
||||
// records handled by another provider...
|
||||
);
|
||||
```
|
||||
|
||||
## Special Windows stuff
|
||||
|
||||
This provider needs to do 2 things:
|
||||
|
||||
* Get a list of zone records:
|
||||
* powerShellDump: Runs a PS command that dumps the zone to JSON.
|
||||
* readZoneDump: Opens a adzonedump.$DOMAINNAME.json file and reads JSON out of it. If the file does not exist, this is considered an error and processing stops.
|
||||
|
||||
* Update records:
|
||||
* powerShellExec: Execute PS commands that do the update.
|
||||
* powerShellRecord: Record the PS command that can be run later to do the updates. This file is -psout=dns_update_commands.ps1
|
||||
|
||||
So what happens when? Well, that's complex. We want both Windows and Linux to be able to use -fakewindows
|
||||
for either debugging or (on Windows) actual use. However only Windows permits -fakewinows=false and actually executes
|
||||
the PS code. Here's which algorithm is used for each case:
|
||||
|
||||
* If -fakewindows is used on any system: readZoneDump and powerShellRecord is used.
|
||||
* On Windows (without -fakewindows): powerShellDump and powerShellExec is used.
|
||||
* On Linux (wihtout -fakewindows): the provider loads as "NONE" and nothing happens.
|
||||
|
||||
|
||||
## Note to future devs
|
||||
|
||||
### Why doesn't this provider delete records?
|
||||
|
||||
Because at this time Stack doesn't fully control AD zones
|
||||
using dnscontrol. It only needs to add/change records.
|
||||
|
||||
What should we do when it does need to delete them?
|
||||
|
||||
Currently NO_PURGE is a no-op. I would change it to update
|
||||
domain metadata to flag that deletes should be enabled/disabled.
|
||||
Then generate the deletes only if this flag exists. To be paranoid,
|
||||
the func that does the deleting could check this flag to make sure
|
||||
that it really should be deleting something.
|
|
@ -1,358 +0,0 @@
|
|||
package activedir
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
||||
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
|
||||
"github.com/TomOnTime/utfutil"
|
||||
)
|
||||
|
||||
const zoneDumpFilenamePrefix = "adzonedump"
|
||||
|
||||
// RecordConfigJSON RecordConfig, reconfigured for JSON input/output.
|
||||
type RecordConfigJSON struct {
|
||||
Name string `json:"hostname"`
|
||||
Type string `json:"recordtype"`
|
||||
Data string `json:"recorddata"`
|
||||
TTL uint32 `json:"timetolive"`
|
||||
}
|
||||
|
||||
func (c *activedirProvider) GetNameservers(string) ([]*models.Nameserver, error) {
|
||||
// TODO: If using AD for publicly hosted zones, probably pull these from config.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// list of types this provider supports.
|
||||
// until it is up to speed with all the built-in types.
|
||||
var supportedTypes = map[string]bool{
|
||||
"A": true,
|
||||
"AAAA": true,
|
||||
"CNAME": true,
|
||||
"NS": true,
|
||||
}
|
||||
|
||||
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
|
||||
func (c *activedirProvider) GetZoneRecords(domain string) (models.Records, error) {
|
||||
foundRecords, err := c.getExistingRecords(domain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("c.getExistingRecords(%q) failed: %v", domain, err)
|
||||
}
|
||||
return foundRecords, nil
|
||||
}
|
||||
|
||||
// GetDomainCorrections gets existing records, diffs them against existing, and returns corrections.
|
||||
func (c *activedirProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
||||
|
||||
dc.Filter(func(r *models.RecordConfig) bool {
|
||||
if r.Type == "NS" && r.Name == "@" {
|
||||
return false
|
||||
}
|
||||
if !supportedTypes[r.Type] {
|
||||
printer.Warnf("Active Directory only manages certain record types. Won't consider %s %s\n", r.Type, r.GetLabelFQDN())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// Read foundRecords:
|
||||
foundRecords, err := c.getExistingRecords(dc.Name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("c.getExistingRecords(%v) failed: %v", dc.Name, err)
|
||||
}
|
||||
|
||||
// Normalize
|
||||
models.PostProcessRecords(foundRecords)
|
||||
|
||||
differ := diff.New(dc)
|
||||
_, creates, dels, modifications, err := differ.IncrementalDiff(foundRecords)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// NOTE(tlim): This provider does not delete records. If
|
||||
// you need to delete a record, either delete it manually
|
||||
// or see providers/activedir/doc.md for implementation tips.
|
||||
|
||||
// Generate changes.
|
||||
corrections := []*models.Correction{}
|
||||
for _, del := range dels {
|
||||
corrections = append(corrections, c.deleteRec(dc.Name, del))
|
||||
}
|
||||
for _, cre := range creates {
|
||||
corrections = append(corrections, c.createRec(dc.Name, cre)...)
|
||||
}
|
||||
for _, m := range modifications {
|
||||
corrections = append(corrections, c.modifyRec(dc.Name, m))
|
||||
}
|
||||
return corrections, nil
|
||||
|
||||
}
|
||||
|
||||
// zoneDumpFilename returns the filename to use to write or read
|
||||
// an activedirectory zone dump for a particular domain.
|
||||
func zoneDumpFilename(domainname string) string {
|
||||
return zoneDumpFilenamePrefix + "." + domainname + ".json"
|
||||
}
|
||||
|
||||
// readZoneDump reads a pre-existing zone dump from adzonedump.*.json.
|
||||
func (c *activedirProvider) readZoneDump(domainname string) ([]byte, error) {
|
||||
// File not found is considered an error.
|
||||
dat, err := utfutil.ReadFile(zoneDumpFilename(domainname), utfutil.WINDOWS)
|
||||
if err != nil {
|
||||
printer.Printf("Powershell to generate zone dump:\n")
|
||||
printer.Printf("%v\n", c.generatePowerShellZoneDump(domainname))
|
||||
}
|
||||
return dat, err
|
||||
}
|
||||
|
||||
// powerShellLogCommand logs to flagPsLog that a PowerShell command is going to be run.
|
||||
func (c *activedirProvider) logCommand(command string) error {
|
||||
return c.logHelper(fmt.Sprintf("# %s\r\n%s\r\n", time.Now().UTC(), strings.TrimSpace(command)))
|
||||
}
|
||||
|
||||
// powerShellLogOutput logs to flagPsLog that a PowerShell command is going to be run.
|
||||
func (c *activedirProvider) logOutput(s string) error {
|
||||
return c.logHelper(fmt.Sprintf("OUTPUT: START\r\n%s\r\nOUTPUT: END\r\n", s))
|
||||
}
|
||||
|
||||
// powerShellLogErr logs that a PowerShell command had an error.
|
||||
func (c *activedirProvider) logErr(e error) error {
|
||||
err := c.logHelper(fmt.Sprintf("ERROR: %v\r\r", e)) // Log error to powershell.log
|
||||
if err != nil {
|
||||
return err // Bubble up error created in logHelper
|
||||
}
|
||||
return e // Bubble up original error
|
||||
}
|
||||
|
||||
func (c *activedirProvider) logHelper(s string) error {
|
||||
logfile, err := os.OpenFile(c.psLog, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0660)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error: Can not create/append to %#v: %v", c.psLog, err)
|
||||
}
|
||||
_, err = fmt.Fprintln(logfile, s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("append to %#v failed: %v", c.psLog, err)
|
||||
}
|
||||
if logfile.Close() != nil {
|
||||
return fmt.Errorf("closing %#v failed: %v", c.psLog, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// powerShellRecord records that a PowerShell command should be executed later.
|
||||
func (c *activedirProvider) powerShellRecord(command string) error {
|
||||
recordfile, err := os.OpenFile(c.psOut, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0660)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can not create/append to %#v: %v", c.psOut, err)
|
||||
}
|
||||
_, err = recordfile.WriteString(command)
|
||||
if err != nil {
|
||||
return fmt.Errorf("append to %#v failed: %v", c.psOut, err)
|
||||
}
|
||||
return recordfile.Close()
|
||||
}
|
||||
|
||||
func (c *activedirProvider) getExistingRecords(domainname string) ([]*models.RecordConfig, error) {
|
||||
// Get the JSON either from adzonedump or by running a PowerShell script.
|
||||
data, err := c.getRecords(domainname)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getRecords failed on %#v: %v", domainname, err)
|
||||
}
|
||||
|
||||
var recs []*RecordConfigJSON
|
||||
jdata := string(data)
|
||||
// when there is only a single record, AD powershell does not
|
||||
// wrap it in an array as our types expect. This makes sure it is always an array.
|
||||
if strings.HasPrefix(strings.TrimSpace(jdata), "{") {
|
||||
jdata = "[" + jdata + "]"
|
||||
data = []byte(jdata)
|
||||
}
|
||||
err = json.Unmarshal(data, &recs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json.Unmarshal failed on %#v: %v", domainname, err)
|
||||
}
|
||||
|
||||
result := make([]*models.RecordConfig, 0, len(recs))
|
||||
unsupportedCounts := map[string]int{}
|
||||
for _, rec := range recs {
|
||||
t, supportedType := rec.unpackRecord(domainname)
|
||||
if !supportedType {
|
||||
unsupportedCounts[rec.Type]++
|
||||
}
|
||||
if t != nil {
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
for t, count := range unsupportedCounts {
|
||||
printer.Warnf("%d records of type %s found in AD zone. These will be ignored.\n", count, t)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *RecordConfigJSON) unpackRecord(origin string) (rc *models.RecordConfig, supported bool) {
|
||||
rc = &models.RecordConfig{
|
||||
Type: r.Type,
|
||||
TTL: r.TTL,
|
||||
}
|
||||
rc.SetLabel(r.Name, origin)
|
||||
switch rtype := rc.Type; rtype { // #rtype_variations
|
||||
case "A", "AAAA":
|
||||
rc.SetTarget(r.Data)
|
||||
case "CNAME":
|
||||
rc.SetTarget(strings.ToLower(r.Data))
|
||||
case "NS":
|
||||
// skip root NS
|
||||
if rc.Name == "@" {
|
||||
return nil, true
|
||||
}
|
||||
rc.SetTarget(strings.ToLower(r.Data))
|
||||
case "SOA":
|
||||
return nil, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
return rc, true
|
||||
}
|
||||
|
||||
// powerShellDump runs a PowerShell command to get a dump of all records in a DNS zone.
|
||||
func (c *activedirProvider) generatePowerShellZoneDump(domainname string) string {
|
||||
cmdTxt := `@("REPLACE_WITH_ZONE") | %{
|
||||
Get-DnsServerResourceRecord -ComputerName REPLACE_WITH_COMPUTER_NAME -ZoneName $_ | select hostname,recordtype,@{n="timestamp";e={$_.timestamp.tostring()}},@{n="timetolive";e={$_.timetolive.totalseconds}},@{n="recorddata";e={($_.recorddata.ipv4address,$_.recorddata.ipv6address,$_.recorddata.HostNameAlias,$_.recorddata.NameServer,"unsupported_record_type" -ne $null)[0]-as [string]}} | ConvertTo-Json > REPLACE_WITH_FILENAMEPREFIX.REPLACE_WITH_ZONE.json
|
||||
}`
|
||||
cmdTxt = strings.Replace(cmdTxt, "REPLACE_WITH_ZONE", domainname, -1)
|
||||
cmdTxt = strings.Replace(cmdTxt, "REPLACE_WITH_COMPUTER_NAME", c.adServer, -1)
|
||||
cmdTxt = strings.Replace(cmdTxt, "REPLACE_WITH_FILENAMEPREFIX", zoneDumpFilenamePrefix, -1)
|
||||
return cmdTxt
|
||||
}
|
||||
|
||||
// generatePowerShellCreate generates PowerShell commands to ADD a record.
|
||||
func (c *activedirProvider) generatePowerShellCreate(domainname string, rec *models.RecordConfig) string {
|
||||
content := rec.GetTargetField()
|
||||
text := "\r\n" // Skip a line.
|
||||
funcSuffix := rec.Type
|
||||
if rec.Type == "NS" {
|
||||
funcSuffix = ""
|
||||
}
|
||||
text += fmt.Sprintf("Add-DnsServerResourceRecord%s", funcSuffix)
|
||||
text += fmt.Sprintf(` -ComputerName "%s"`, c.adServer)
|
||||
text += fmt.Sprintf(` -ZoneName "%s"`, domainname)
|
||||
text += fmt.Sprintf(` -Name "%s"`, rec.GetLabel())
|
||||
text += fmt.Sprintf(` -TimeToLive $(New-TimeSpan -Seconds %d)`, rec.TTL)
|
||||
switch rec.Type { // #rtype_variations
|
||||
case "CNAME":
|
||||
text += fmt.Sprintf(` -HostNameAlias "%s"`, content)
|
||||
case "A":
|
||||
text += fmt.Sprintf(` -IPv4Address "%s"`, content)
|
||||
case "NS":
|
||||
text += fmt.Sprintf(` -NS -NameServer "%s"`, content)
|
||||
default:
|
||||
panic(fmt.Errorf("generatePowerShellCreate() does not yet handle recType=%s recName=%#v content=%#v)",
|
||||
rec.Type, rec.GetLabel(), content))
|
||||
// We panic so that we quickly find any switch statements
|
||||
// that have not been updated for a new RR type.
|
||||
}
|
||||
text += "\r\n"
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
// generatePowerShellModify generates PowerShell commands to MODIFY a record.
|
||||
func (c *activedirProvider) generatePowerShellModify(domainname, recName, recType, oldContent, newContent string, oldTTL, newTTL uint32) string {
|
||||
|
||||
var queryField, queryContent string
|
||||
queryContent = `"` + oldContent + `"`
|
||||
|
||||
switch recType { // #rtype_variations
|
||||
case "A":
|
||||
queryField = "IPv4address"
|
||||
case "CNAME":
|
||||
queryField = "HostNameAlias"
|
||||
case "NS":
|
||||
queryField = "NameServer"
|
||||
default:
|
||||
panic(fmt.Errorf("generatePowerShellModify() does not yet handle recType=%s recName=%#v content=(%#v, %#v)", recType, recName, oldContent, newContent))
|
||||
// We panic so that we quickly find any switch statements
|
||||
// that have not been updated for a new RR type.
|
||||
}
|
||||
|
||||
text := "\r\n" // Skip a line.
|
||||
text += fmt.Sprintf(`echo "MODIFY %s %s %s old=%s new=%s"`, recName, domainname, recType, oldContent, newContent)
|
||||
text += "\r\n"
|
||||
|
||||
text += "$OldObj = Get-DnsServerResourceRecord"
|
||||
text += fmt.Sprintf(` -ComputerName "%s"`, c.adServer)
|
||||
text += fmt.Sprintf(` -ZoneName "%s"`, domainname)
|
||||
text += fmt.Sprintf(` -Name "%s"`, recName)
|
||||
text += fmt.Sprintf(` -RRType "%s"`, recType)
|
||||
text += fmt.Sprintf(" | Where-Object {$_.RecordData.%s -eq %s -and $_.HostName -eq \"%s\"}", queryField, queryContent, recName)
|
||||
text += "\r\n"
|
||||
text += `if($OldObj.Length -ne $null){ throw "Error, multiple results for Get-DnsServerResourceRecord" }`
|
||||
text += "\r\n"
|
||||
|
||||
text += "$NewObj = $OldObj.Clone()"
|
||||
text += "\r\n"
|
||||
|
||||
if oldContent != newContent {
|
||||
text += fmt.Sprintf(`$NewObj.RecordData.%s = "%s"`, queryField, newContent)
|
||||
text += "\r\n"
|
||||
}
|
||||
|
||||
if oldTTL != newTTL {
|
||||
text += fmt.Sprintf(`$NewObj.TimeToLive = New-TimeSpan -Seconds %d`, newTTL)
|
||||
text += "\r\n"
|
||||
}
|
||||
|
||||
text += "Set-DnsServerResourceRecord"
|
||||
text += fmt.Sprintf(` -ComputerName "%s"`, c.adServer)
|
||||
text += fmt.Sprintf(` -ZoneName "%s"`, domainname)
|
||||
text += ` -NewInputObject $NewObj -OldInputObject $OldObj`
|
||||
text += "\r\n"
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func (c *activedirProvider) generatePowerShellDelete(domainname, recName, recType, content string) string {
|
||||
text := fmt.Sprintf(`echo "DELETE %s %s %s"`, recType, recName, content)
|
||||
text += "\r\n"
|
||||
text += `Remove-DnsServerResourceRecord -Force -ComputerName "%s" -ZoneName "%s" -Name "%s" -RRType "%s" -RecordData "%s"`
|
||||
text += "\r\n"
|
||||
return fmt.Sprintf(text, c.adServer, domainname, recName, recType, content)
|
||||
}
|
||||
|
||||
func (c *activedirProvider) createRec(domainname string, cre diff.Correlation) []*models.Correction {
|
||||
rec := cre.Desired
|
||||
arr := []*models.Correction{
|
||||
{
|
||||
Msg: cre.String(),
|
||||
F: func() error {
|
||||
return c.powerShellDoCommand(c.generatePowerShellCreate(domainname, rec), true)
|
||||
}},
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
func (c *activedirProvider) modifyRec(domainname string, m diff.Correlation) *models.Correction {
|
||||
old, rec := m.Existing, m.Desired
|
||||
return &models.Correction{
|
||||
Msg: m.String(),
|
||||
F: func() error {
|
||||
return c.powerShellDoCommand(c.generatePowerShellModify(domainname, rec.GetLabel(), rec.Type, old.GetTargetField(), rec.GetTargetField(), old.TTL, rec.TTL), true)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *activedirProvider) deleteRec(domainname string, cor diff.Correlation) *models.Correction {
|
||||
rec := cor.Existing
|
||||
return &models.Correction{
|
||||
Msg: cor.String(),
|
||||
F: func() error {
|
||||
return c.powerShellDoCommand(c.generatePowerShellDelete(domainname, rec.GetLabel(), rec.Type, rec.GetTargetField()), true)
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package activedir
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
)
|
||||
|
||||
func makeRC(label, domain, target string, rc models.RecordConfig) *models.RecordConfig {
|
||||
rc.SetLabel(label, domain)
|
||||
rc.SetTarget(target)
|
||||
return &rc
|
||||
}
|
||||
|
||||
func TestGetExistingRecords(t *testing.T) {
|
||||
|
||||
cf := &activedirProvider{}
|
||||
|
||||
cf.fake = true
|
||||
actual, err := cf.getExistingRecords("test2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []*models.RecordConfig{
|
||||
makeRC("@", "test2", "10.166.2.11", models.RecordConfig{Type: "A", TTL: 600}),
|
||||
makeRC("_msdcs", "test2", "other_record", models.RecordConfig{Type: "NS", TTL: 300}),
|
||||
makeRC("co-devsearch02", "test2", "10.8.2.64", models.RecordConfig{Type: "A", TTL: 3600}),
|
||||
makeRC("co-devservice01", "test2", "10.8.2.48", models.RecordConfig{Type: "A", TTL: 1200}), // Downcased.
|
||||
makeRC("yum", "test2", "10.8.0.59", models.RecordConfig{Type: "A", TTL: 3600}),
|
||||
}
|
||||
|
||||
actualS := ""
|
||||
for i, x := range actual {
|
||||
actualS += fmt.Sprintf("%d %v\n", i, x)
|
||||
}
|
||||
|
||||
expectedS := ""
|
||||
for i, x := range expected {
|
||||
expectedS += fmt.Sprintf("%d %v\n", i, x)
|
||||
}
|
||||
|
||||
if actualS != expectedS {
|
||||
t.Fatalf("got\n(%s)\nbut expected\n(%s)", actualS, expectedS)
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package activedir
|
||||
|
||||
func (c *activedirProvider) getRecords(domainname string) ([]byte, error) {
|
||||
if !c.fake {
|
||||
panic("Can not happen: PowerShell on non-windows")
|
||||
}
|
||||
return c.readZoneDump(domainname)
|
||||
}
|
||||
|
||||
func (c *activedirProvider) powerShellDoCommand(command string, shouldLog bool) error {
|
||||
if !c.fake {
|
||||
panic("Can not happen: PowerShell on non-windows")
|
||||
}
|
||||
return c.powerShellRecord(command)
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
package activedir
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/pkg/printer"
|
||||
)
|
||||
|
||||
var checkPS sync.Once
|
||||
var psAvailible = false
|
||||
|
||||
func (c *activedirProvider) getRecords(domainname string) ([]byte, error) {
|
||||
|
||||
// If we are using PowerShell, make sure it is enabled
|
||||
// and then run the PS1 command to generate the adzonedump file.
|
||||
|
||||
if !c.fake {
|
||||
checkPS.Do(func() {
|
||||
psAvailible = c.isPowerShellReady()
|
||||
if !psAvailible {
|
||||
printer.Printf("\n\n\n")
|
||||
printer.Printf("***********************************************\n")
|
||||
printer.Printf("PowerShell DnsServer module not installed.\n")
|
||||
printer.Printf("See http://social.technet.microsoft.com/wiki/contents/articles/2202.remote-server-administration-tools-rsat-for-windows-client-and-windows-server-dsforum2wiki.aspx\n")
|
||||
printer.Printf("***********************************************\n")
|
||||
printer.Printf("\n\n\n")
|
||||
}
|
||||
})
|
||||
if !psAvailible {
|
||||
return nil, fmt.Errorf("powershell module DnsServer not installed")
|
||||
}
|
||||
|
||||
_, err := c.powerShellExec(c.generatePowerShellZoneDump(domainname), true)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
}
|
||||
// Return the contents of zone.*.json file instead.
|
||||
return c.readZoneDump(domainname)
|
||||
}
|
||||
|
||||
func (c *activedirProvider) isPowerShellReady() bool {
|
||||
query, _ := c.powerShellExec(`(Get-Module -ListAvailable DnsServer) -ne $null`, true)
|
||||
q, err := strconv.ParseBool(strings.TrimSpace(string(query)))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func (c *activedirProvider) powerShellDoCommand(command string, shouldLog bool) error {
|
||||
if c.fake {
|
||||
// If fake, just record the command.
|
||||
return c.powerShellRecord(command)
|
||||
}
|
||||
_, err := c.powerShellExec(command, shouldLog)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *activedirProvider) powerShellExec(command string, shouldLog bool) ([]byte, error) {
|
||||
// log it.
|
||||
err := c.logCommand(command)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Run it.
|
||||
out, err := exec.Command("powershell", "-NoProfile", command).CombinedOutput()
|
||||
if err != nil {
|
||||
// If there was an error, log it.
|
||||
c.logErr(err)
|
||||
}
|
||||
if shouldLog {
|
||||
err = c.logOutput(string(out))
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Return the result.
|
||||
return out, err
|
||||
}
|
Binary file not shown.
Loading…
Add table
Reference in a new issue