Skip to content

Commit 0bbd054

Browse files
authored
Merge pull request #4 from silinternational/feature/dns-from-tfc
Use Terraform state output for DNS values
2 parents 5adbb41 + fcaf173 commit 0bbd054

File tree

4 files changed

+120
-49
lines changed

4 files changed

+120
-49
lines changed

cmd/cli/multiregion/dns.go

+92-24
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"log"
1111

1212
"github.com/cloudflare/cloudflare-go"
13+
"github.com/hashicorp/go-tfe"
1314
"github.com/spf13/cobra"
1415
"github.com/spf13/viper"
1516
)
@@ -18,26 +19,49 @@ type DnsCommand struct {
1819
cfClient *cloudflare.API
1920
cfZone *cloudflare.ResourceContainer
2021
domainName string
22+
tfcOrg string
23+
tfcToken string
2124
testMode bool
2225
}
2326

27+
type AlbDnsValues struct {
28+
internal string
29+
external string
30+
}
31+
2432
func InitDnsCmd(parentCmd *cobra.Command) {
25-
parentCmd.AddCommand(&cobra.Command{
33+
var failback bool
34+
35+
cmd := &cobra.Command{
2636
Use: "dns",
2737
Short: "DNS Failover and Failback",
28-
Long: `Configure DNS CNAME values for primary or secondary region hostnames`,
38+
Long: `Configure DNS CNAME values for primary or secondary region hostnames. Default is failover, use --failback to switch back to the primary region.`,
2939
Run: func(cmd *cobra.Command, args []string) {
30-
runDnsCommand()
40+
runDnsCommand(failback)
3141
},
32-
})
42+
}
43+
parentCmd.AddCommand(cmd)
44+
45+
cmd.PersistentFlags().BoolVar(&failback, "failback", false,
46+
`set DNS records to switch back to primary`,
47+
)
3348
}
3449

35-
func runDnsCommand() {
50+
func runDnsCommand(failback bool) {
3651
pFlags := getPersistentFlags()
3752

38-
f := newDnsCommand(pFlags)
53+
d := newDnsCommand(pFlags)
54+
55+
var clusterWorkspaceName string
56+
if failback {
57+
clusterWorkspaceName = clusterWorkspace(pFlags)
58+
} else {
59+
clusterWorkspaceName = clusterSecondaryWorkspace(pFlags)
60+
}
3961

40-
f.setDnsToSecondary(pFlags.idp)
62+
dnsValues := d.getAlbDnsValuesFromTfc(clusterWorkspaceName)
63+
64+
d.setDnsRecordValues(pFlags.idp, dnsValues, failback)
4165
}
4266

4367
func newDnsCommand(pFlags PersistentFlags) *DnsCommand {
@@ -68,38 +92,44 @@ func newDnsCommand(pFlags PersistentFlags) *DnsCommand {
6892
fmt.Printf("Using domain name %s with ID %s\n", d.domainName, zoneID)
6993
d.cfZone = cloudflare.ZoneIdentifier(zoneID)
7094

95+
d.tfcToken = pFlags.tfcToken
96+
d.tfcOrg = pFlags.org
97+
7198
return &d
7299
}
73100

74-
func (d *DnsCommand) setDnsToSecondary(idpKey string) {
75-
fmt.Println("Setting DNS records to secondary...")
101+
func (d *DnsCommand) setDnsRecordValues(idpKey string, dnsValues AlbDnsValues, failback bool) {
102+
if failback {
103+
fmt.Println("Setting DNS records to primary region...")
104+
} else {
105+
fmt.Println("Setting DNS records to secondary region...")
106+
}
76107

77108
dnsRecords := []struct {
78-
optionName string
79-
defaultName string
80-
optionValue string
109+
name string
110+
optionValue string
111+
defaultValue string
81112
}{
82113
// "mfa-api" is the TOTP API, also known as serverless-mfa-api
83-
{"mfa-api-name", "mfa-api", "mfa-api-value"},
114+
{"mfa-api", "mfa-api-value", ""},
84115

85116
// "twosv-api" is the Webauthn API, also known as serverless-mfa-api-go
86-
{"twosv-api-name", "twosv-api", "twosv-api-value"},
117+
{"twosv-api", "twosv-api-value", ""},
87118

88119
// "support-bot" is the idp-support-bot API that is configured in the Slack API dashboard
89-
{"support-bot-name", "sherlock", "support-bot-value"},
120+
{"sherlock", "support-bot-value", ""},
90121

91122
// ECS services
92-
{"email-service-name", idpKey + "-email-service", "email-service-value"},
93-
{"id-broker-name", idpKey + "-id-broker", "id-broker-value"},
94-
{"pw-api-name", idpKey + "-pw-api", "pw-api-value"},
95-
{"ssp-name", idpKey + "-ssp", "ssp-value"},
96-
{"id-sync-name", idpKey + "-id-sync", "id-sync-value"},
123+
{idpKey + "-email", "email-service-value", dnsValues.internal},
124+
{idpKey + "-broker", "id-broker-value", dnsValues.internal},
125+
{idpKey + "-pw-api", "pw-api-value", dnsValues.external},
126+
{idpKey, "ssp-value", dnsValues.external},
127+
{idpKey + "-sync", "id-sync-value", dnsValues.external},
97128
}
98129

99130
for _, record := range dnsRecords {
100-
name := getOption(record.optionName, record.defaultName)
101-
value := getOption(record.optionValue, "")
102-
d.setCloudflareCname(name, value)
131+
value := getOption(record.optionValue, record.defaultValue)
132+
d.setCloudflareCname(record.name, value)
103133
}
104134
}
105135

@@ -127,7 +157,7 @@ func (d *DnsCommand) setCloudflareCname(name, value string) {
127157
}
128158

129159
if d.testMode {
130-
fmt.Println(" test mode: skipping API call")
160+
fmt.Println(" read-only mode: skipping API call")
131161
return
132162
}
133163

@@ -146,3 +176,41 @@ func (d *DnsCommand) setCloudflareCname(name, value string) {
146176
log.Fatalf("error updating DNS record %s: %s", name, err)
147177
}
148178
}
179+
180+
func (d *DnsCommand) getAlbDnsValuesFromTfc(workspaceName string) (values AlbDnsValues) {
181+
config := &tfe.Config{
182+
Token: d.tfcToken,
183+
RetryServerErrors: true,
184+
}
185+
186+
client, err := tfe.NewClient(config)
187+
if err != nil {
188+
fmt.Printf("Error creating Terraform client: %s", err)
189+
return
190+
}
191+
192+
ctx := context.Background()
193+
194+
w, err := client.Workspaces.Read(ctx, d.tfcOrg, workspaceName)
195+
if err != nil {
196+
fmt.Printf("Error reading Terraform workspace %s: %s", workspaceName, err)
197+
return
198+
}
199+
200+
outputs, err := client.StateVersionOutputs.ReadCurrent(ctx, w.ID)
201+
if err != nil {
202+
fmt.Printf("Error reading Terraform state outputs on workspace %s: %s", workspaceName, err)
203+
return
204+
}
205+
206+
for _, item := range outputs.Items {
207+
itemValue, _ := item.Value.(string)
208+
switch item.Name {
209+
case "alb_dns_name":
210+
values.external = itemValue
211+
case "internal_alb_dns_name":
212+
values.internal = itemValue
213+
}
214+
}
215+
return
216+
}

go.mod

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ go 1.20
66

77
require (
88
github.com/cloudflare/cloudflare-go v0.69.0
9+
github.com/hashicorp/go-tfe v1.31.0
910
github.com/silinternational/tfc-ops/v3 v3.5.0
1011
github.com/spf13/cobra v1.7.0
1112
github.com/spf13/viper v1.16.0
@@ -16,8 +17,11 @@ require (
1617
github.com/fsnotify/fsnotify v1.6.0 // indirect
1718
github.com/google/go-querystring v1.1.0 // indirect
1819
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
19-
github.com/hashicorp/go-retryablehttp v0.7.3 // indirect
20+
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
21+
github.com/hashicorp/go-slug v0.12.0 // indirect
22+
github.com/hashicorp/go-version v1.6.0 // indirect
2023
github.com/hashicorp/hcl v1.0.0 // indirect
24+
github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d // indirect
2125
github.com/inconshreveable/mousetrap v1.1.0 // indirect
2226
github.com/magiconair/properties v1.8.7 // indirect
2327
github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -28,6 +32,7 @@ require (
2832
github.com/spf13/pflag v1.0.5 // indirect
2933
github.com/subosito/gotenv v1.4.2 // indirect
3034
golang.org/x/net v0.10.0 // indirect
35+
golang.org/x/sync v0.3.0 // indirect
3136
golang.org/x/sys v0.9.0 // indirect
3237
golang.org/x/text v0.10.0 // indirect
3338
golang.org/x/time v0.3.0 // indirect

go.sum

+13-2
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,21 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
128128
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
129129
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
130130
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
131-
github.com/hashicorp/go-retryablehttp v0.7.3 h1:5n2R5B7+2YWrtEzIWO2jPPQWseAPLkgIuvUgS1l97KY=
132-
github.com/hashicorp/go-retryablehttp v0.7.3/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
131+
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
132+
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
133+
github.com/hashicorp/go-slug v0.12.0 h1:y1ArGp5RFF85uvD8nq5VZug/bup/kGN5Ft4xFOQ5GPM=
134+
github.com/hashicorp/go-slug v0.12.0/go.mod h1:JZVtycnZZbiJ4oxpJ/zfhyfBD8XxT4f0uOSyjNLCqFY=
135+
github.com/hashicorp/go-tfe v1.31.0 h1:R1CokrAVBHxrsvRw1vKes7RQxTRTWcula7gjQK7Jfsk=
136+
github.com/hashicorp/go-tfe v1.31.0/go.mod h1:vcfy2u52JQ4sYLFi941qcQXQYfUq2RjEW466tZ+m97Y=
137+
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
138+
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
139+
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
133140
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
134141
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
135142
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
136143
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
144+
github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d h1:9ARUJJ1VVynB176G1HCwleORqCaXm/Vx0uUi0dL26I0=
145+
github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik=
137146
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
138147
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
139148
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -293,6 +302,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
293302
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
294303
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
295304
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
305+
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
306+
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
296307
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
297308
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
298309
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

idp-cli-example.toml

+9-22
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
idp = "myidp"
66

77
# -------------------------------------------------------------------------------------------------
8-
# These parameters are required for the "multiregion setup" and "multiregion failover" commands.
8+
# These parameters are required for multiregion commands.
99

1010
# Operation environment, used for Terraform workspace name. Default is "prod"
1111
env = "prod"
@@ -20,43 +20,30 @@ org = "my-tfc-org"
2020
tfc-token = ""
2121

2222
# -------------------------------------------------------------------------------------------------
23-
# These parameters are required for the "dns" commands.
23+
# These additional parameters are for the "multiregion dns" command.
2424

25+
# "domain-name" is required
2526
domain-name = "example.net"
27+
28+
# "cloudflare-token" is required and must have edit permission on the domain name specified in "domain-name"
2629
cloudflare-token = ""
2730

28-
# The "name" parameters specify the DNS subdomain.
29-
# The "value" parameters specify the hostname.
31+
# The following are the values to set DNS CNAME records. If any DNS value is omitted, the "multiregion dns" command
32+
# will skip that record update.
3033

3134
# "mfa-api" is the TOTP API, also known as serverless-mfa-api.
32-
# The default mfa-api-name is "mfa-api".
33-
mfa-api-name = "mfa-api"
3435
mfa-api-value = "abcde01234.execute-api.us-west-2.amazonaws.com"
3536

3637
# "twosv-api" is the Webauthn API, also known as serverless-mfa-api-go.
37-
# The default twosv-api-name is "twosv-api".
38-
twosv-api-name = "twosv-api"
3938
twosv-api-value = "abcde12345.execute-api.us-west-2.amazonaws.com"
4039

4140
# "support-bot" is the idp-support-bot API that is configured in the Slack API dashboard.
42-
# The default support-bot-value is "sherlock".
43-
support-bot-name = "sherlock"
4441
support-bot-value = "abcde23456.execute-api.us-west-2.amazonaws.com"
4542

46-
# These are the DNS name and value for each of the ECS services.
47-
# The default name for each is the IdP key followed by the service name.
48-
49-
email-service-name = "myidp-email-service"
43+
# These are the DNS values for each of the ECS services. If omitted, the value will be read from the
44+
# Terraform Cloud "010-cluster" workspace output.
5045
email-service-value = "internal-alb-idp-myidp-prod-int-123456789.us-west-2.elb.amazonaws.com"
51-
52-
id-broker-name = "myidp-broker"
5346
id-broker-value = "internal-alb-idp-myidp-prod-int-123456789.us-west-2.elb.amazonaws.com"
54-
55-
pw-api-name = "myidp-pw-api"
5647
pw-api-value = "alb-idp-myidp-prod-987654321-us-west-2.elb.amazonaws.com"
57-
58-
ssp-name = "myidp"
5948
ssp-value = "alb-idp-myidp-prod-987654321-us-west-2.elb.amazonaws.com"
60-
61-
id-sync-name = "myidp-sync"
6249
id-sync-value = "alb-idp-myidp-prod-987654321-us-west-2.elb.amazonaws.com"

0 commit comments

Comments
 (0)